/**
 * Created by Loriling on 2016/7/20.
 */
define(['js/lib/async.min',
    'js/workorder/nextstepinfo3',
    'js/workorder/struct3',
    'js/workorder/buttonstatus3',
    'js/objective' + $E.jsv,
    'js/customer' + $E.jsv,
    'js/templetUtil' + $E.jsv], function (async, NextStepInfo, Struct, ButtonStatus, Objective, Customer, MergeUtil) {

    /**
     * 工单3
     * insert into sysc_addins (ADDINNAME, ADDINDESC, TOOLTIP, MULTIPLE, HTMLTYPE, PLUGINNOTES) values ('AddinWO3', '核心组件-工单3', '工单3', 1, 'WO3', null);
     */

    const WOUtils = {
        PROPERTY : {
            SAVE_STATUS : "Temp.WorkOrderSaveStatus",
            SEND_STATUS : "Temp.WorkOrderSendStatus",
            CLOSE_OR_REOPEN_STATUS : "Temp.WOCloseReOpenStatus",
            END_STATUS : "Temp.WOEndStatus",
            REOPEN_STATUS : "Temp.WOReOpenStatus",
            CANCEL_STATUS : "Temp.WorkOrderCancelStatus",
            FAILED_STATUS : "Temp.WorkOrderFailedStatus",
            TAKEOVER_STATUS : "Temp.WorkOrderTakeOverStatus",
            CD_STATUS : "Temp.WorkOrderCDStatus",
            BACK_STATUS : "Temp.WorkOrderBackStatus",
            REPLY_STATUS : "Temp.WorkOrderReplyStatus",
            REVOKE_STATUS : 'Temp.WorkOrderRevokeStatus',//撤单状态属性
            TRANSFER_STATUS : 'Temp.WorkOrderTransferStatus',//转交状态属性
            ASSIST_STATUS : 'Temp.WorkOrderAssistStatus',//协办状态属性
            CC_READ_STATUS: 'Temp.WorkOrderCCReadStatus',//抄送已读状态属性

            CLOSE_FLAG : "Temp.WorkOrderCloseFlag",//工单是否关闭标识
            CURRENT_HANDLE_GRP : "Temp.WoCurrentGrp",//当前处理组
            CURRENT_HANDLE_BY : "Temp.WoCurrentBy",//当前处理人
            NEXT_STEP_ID : "Temp.WONextStepID",//指定下一步步骤
            NEXT_STEP_GROUP_ID : "Temp.WoSendNextGroupID",//强制下送给XXX组
            NEXT_STEP_STAFF_ID : "Temp.WoSendNextStaffID",//强制下送给XXX组中的XXX人,必须选配组
            NEXT_STEP_ROLE_ID : 'Temp.WoSendNextRoleID',//强制下送给XXX角色
            NEXT_STEP_BEGIN_DATE : "Temp.WOSendStepBeginDate",//下送步骤开始时间
            NEXT_STEP_EXPIRE_DATE : "Temp.WOSendStepExpireDate",//下送步骤过期时间
            NEXT_STEP_HANDLE_TYPE : "Temp.WoSendNextHandleType",//强制下送的handleType
            NEXT_STEP_SEND_TYPE : "Temp.WoSendNextSendType",//强制下送的sendType
            NEXT_STEP_OLD_STEP_ID: "Temp.WoSendNextOldStepId",// 原步骤处理人人步骤
            NEXT_STEP_SEND_TO_GROUP: "Temp.WoSendNextSendToGroup", // 原步骤处理人
            BACK_GRP_ID : "Temp.WoBackGroupID",//强制退回给组
            BACK_STAFF_ID : "Temp.WoBackStaffID",//强制退回给人 (必须要设组，人才有效)
            STEP_COMMENTS : "Temp.WOStepComments",//步骤上的Comments
            STEP_ID : "Temp.WOStepID",
            STEP_STATUS : "Temp.WOStepStatus",

            NEXT_SUB_STEP_INFO : "Temp.WOSubStepInfo",//协办步骤处理信息
            //格式: 步骤ID，0/1(1表示这个协办步骤忽略),处理组，处理人|步骤ID，0/1(1表示这个协办步骤忽略),处理组，处理人|步骤ID，0/1(1表示这个协办步骤忽略),处理组，处理人.....

            FRAME_ID : "Temp.WorkOrderFrameID",
            PRIORITY : "Temp.WorkOrderPriority",
            EXPIRE_DATE : "Temp.WorkOrderExpireDate",
            FORECAST_DATE : "Temp.WOForecastDate",
            STEP_FORECAST_DATE : "Temp.WOStepForecastDate",
            STATUS : "Temp.WorkOrderStatus",
            NEW_FLAG : "Temp.WorkOrderNewFlag",

            TAB_CLOSE : "Temp.WOTabClose",//关闭TAB页
            SAVE : "Temp.WorkOrderSave",
            SEND : "Temp.WorkOrderSend",
            CLOSE_OR_REOPEN : "Temp.WOCloseReOpen",
            END : "Temp.WOEnd",
            REOPEN : "Temp.WOReOpen",
            CANCEL : "Temp.WorkOrderCancel",
            FAILED : "Temp.WorkOrderFailed",
            TAKEOVER : "Temp.WorkOrderTakeOver",
            CD : "Temp.WorkOrderCD",
            BACK : "Temp.WorkOrderBack",
            REPLY : "Temp.WorkOrderReply",
            REVOKE : 'Temp.WorkOrderRevoke',//撤单按钮
            TRANSFER : 'Temp.WorkOrderTransfer',//转交
            ASSIST : 'Temp.WorkOrderAssist',//协办
            CC_READ: 'Temp.WorkOrderCCRead',//抄送已读

            SUB_STATUS : 'Temp.ObjectiveSubStatus',
            FRAME_DESC : 'Temp.WorkOrderFrameDESC',
            STEP_DESC : 'Temp.WOStepDESC',
            TASK_GUID : 'Temp.EliteTask_GUID',
            TASK_CREATED_DATE : 'Temp.WOTaskCreateddate',
            CREATED_DATE : 'Temp.WOCreateddate',
            NODE_TEMPLATE : 'Temp.WONodeTemplate',
            OPERATE_TASK_GUID : 'Temp.OperateTaskGuid',
            REVOKE_TO_NODEMAP_ID : 'Temp.RevokeToNodeMapId',
            NODE_MULTI_TASK : 'Temp.WONodeMultiTask',
            NODE_TASK_FLAG : 'Temp.WONodeTaskFlag',
            OBJECTIVE_STATUS : 'Temp.WOObjectiveStatus',
            ROLEGROUP_ID : 'Temp.WorkOrderRolegroupId'
        },

        //催单接管时候可以看到的人的权限控制，和下送时候下送给的人
        OPERATION_TYPE : {
            ORIGIN : 0, //原来方式
            SPECIFIED_ROLEGROUP : 1, //指定部门
            SPECIFIED_PERSON : 2, //指定人
            SPECIFIED_ROLE : 3, //指定角色
            OLD_HANDLEBY : 4, //原步骤处理人
            OLD_HANDLEGROUP : 5, //原步骤处理组
            DIRECT_SUPERVISOR : 7, //直属上级部门
            ALL_SUPERVISOR : 8 //所有上级部门
        },

        /**
         * 新建路径
         */
        newPath : function (params) {
            const pathBoundData = new BoundData("workorderpath3" + $E.staff.epidSql);
            pathBoundData.notFindDupli = 1;
            pathBoundData.KeyInfoEdit("WORKORDERPATH_GUID");
            pathBoundData.setValue("WORKORDER_GUID", params.workorderGuid);
            pathBoundData.setValue("HANDLEGROUP", params.handleGroupId);
            pathBoundData.setValue("HANDLEBY", params.handleById);
            pathBoundData.setValue("MODIFIEDDATE", "##?#NOWDD#?##", $CONST.BoundType.F_Date);
            pathBoundData.setValue("WORKORDERPATH_GUID", Guid.raw());
            pathBoundData.setValue("WOFRAME_ID", params.frameId);
            pathBoundData.setValue("FROMFRAMEID", params.fromFrameId);
            pathBoundData.setValue("FROM_STEPIDMAP", params.fromStepIdMap);
            pathBoundData.setValue("TO_STEPIDMAP", params.toStepIdMap);
            return pathBoundData;
        },

        /**
         * 新建步骤
         */
        newStep : function (params) {
            const stepBoundData = new BoundData("workorderstep3" + $E.staff.epidSql);
            stepBoundData.notFindDupli = 1;
            stepBoundData.KeyInfoEdit("WORKORDERSTEP_GUID");
            stepBoundData.setValue("WORKORDER_GUID", params.workorderGuid);
            stepBoundData.setValue("WORKORDERSTEP_GUID", params.workorderStepGuid);
            stepBoundData.setValue("OldHANDLEBY_ID", params.oldHandleById);
            stepBoundData.setValue("OldHANDLEGROUP_ID", params.oldHandleGroupId);
            stepBoundData.setValue("HANDLEBY_ID", params.handleById || '');
            stepBoundData.setValue("HANDLEGROUP_ID", params.handleGroupId || '');
            stepBoundData.setValue("WORKORDERSTEPSTATUS", params.workorderStepStatus);
            stepBoundData.setValue("COMMENTS", params.comments);
            stepBoundData.setValue("MODIFIEDDATE", "##?#NOWDD#?##", $CONST.BoundType.F_Date);
            stepBoundData.setValue("FROM_STEPIDMAP", params.fromStepIdMap);
            stepBoundData.setValue("TO_STEPIDMAP", params.toStepIdMap || '');
            if (params.handleStaffId) {
                stepBoundData.setValue("HANDLE_STAFF_ID", params.handleStaffId || '');
            }

            if (params.stepExpired) {
                stepBoundData.setValue("StepExpired", params.stepExpired, $CONST.BoundType.F_Date);
            }
            if (params.startTime) {
                stepBoundData.setValue("StepStart", params.startTime, $CONST.BoundType.F_Date);
            }
            if (params.childId) {
                stepBoundData.setValue("Child_ID", params.childId);
            }
            if (params.sendStepInfo) {
                for (let field in params.sendStepInfo) {
                    let value = params.sendStepInfo[field], type = $CONST.BoundType.F_String;
                    if (typeof(value) === 'object') {
                        type = value.type;
                        value = value.value;
                    }
                    $F.log('Workorder3 - newStep', 'Step field:' + field + ' value:' + value + ' type: ' + type);
                    field = field.toLowerCase();
                    if (['workstarttime', 'workendtime'].includes(field)) {
                        stepBoundData.setValue(field, value, $CONST.BoundType.F_Date);
                    } else if (['cur_expiredflag', 'cur_expiredlen'].includes(field)) {
                        stepBoundData.setValue(field, value, $CONST.BoundType.F_Int);
                    } else {
                        stepBoundData.setValue(field, value, type);
                    }
                }
            }
            if (params.handleRoleId) {
                stepBoundData.setValue("handlerole_id", params.handleRoleId);
            }
            return stepBoundData;
        },

        newCCList: function(params) {
            // ECore.Workorder.insertTaskCCList = INSERT INTO ELITETASK_CCLIST{C_EPID}(CCLIST_GUID, OBJECTIVE_GUID, ELITETASK_GUID, STAFF_ID) VALUES (@{C1}S, @{C2}S, @{C3}S,@{C4}S)
            // this.project.commonUpdateSync("ECore.Workorder.insertTaskCCList", [Guid.raw(), this.guid, taskId, cc], this.dbPool);
            const cclistBoundData = new BoundData("ELITETASK_CCLIST" + $E.staff.epidSql);
            cclistBoundData.KeyInfoEdit("CCLIST_GUID");
            cclistBoundData.setValue("CCLIST_GUID", Guid.raw());
            cclistBoundData.setValue("OBJECTIVE_GUID", params.oId);
            cclistBoundData.setValue("ELITETASK_GUID", params.taskId);
            cclistBoundData.setValue("STAFF_ID", params.cc);
            return cclistBoundData;
        },

        /**
         * 从给定的nodes中找某个id的node
         */
        findNode : function (nodes, id) {
            for (var i = 0; i < nodes.length; i++) {
                if (nodes[i].NODEMAP_ID === id)
                    return nodes[i];
            }
        },

        getFlag : function (vObj, vFlagKey) {
            if (!vObj) {
                return 1;
            }
            var mFlag = vObj[vFlagKey.toUpperCase()];
            if (!mFlag) {
                return 1;
            } else if (typeof(mFlag) === "string") {
                return parseInt(mFlag);
            } else if (typeof(mFlag) === 'number') {
                return mFlag;
            } else {
                return 1;
            }
        }
    };

    const Workorder = function (project, addinId, dbPool) {
        const wo = this;
        this.project = project;
        this.addinId = addinId || 'AddinWO3';
        this.guid = "";
        this.frameId = "";
        this.oldFrameId = "";
        this.oldStepIdMap = "";
        this.struct = {};
        this.showBuildId = false;
        this.instance = null;
        this.newFlag = 0;
        this.woBoundData = new BoundData("objective" + $E.staff.epidSql);
        this.woBoundData.KeyInfoEdit("objective_guid");
        this.woBoundData.setCreateInfo("CREATEDDATE", "CREATEDBY", $E.staff.id);
        this.woBoundData.setModifyInfo("MODIFIEDDATE", "MODIFIEDBY", $E.staff.id);
        this.taskBoundData = new BoundData("elitetask" + $E.staff.epidSql);
        this.taskBoundData.KeyInfoEdit("elitetask_guid");
        this.taskBoundData.setCreateInfo("CREATEDDATE", "CREATEDBY", $E.staff.id);
        this.taskBoundData.setModifyInfo("MODIFIEDDATE", "MODIFIEDBY", $E.staff.id);
        this.buttonStatus = new ButtonStatus(project, wo);
        this.nextStepInfo = new NextStepInfo(project, wo);
        this.currentSubStepID = "";
        this.currentSubStepRunID = "";
        this.operateTaskGuid = '';
        this.taskId = '';
        this.allExtFields = {};
        this.stepFieldInfo = {};
        this.customSubs = [];
        // Fixed by kevin
        // 在新版工单中，不必强制加载附加字段，由项目自行配置在woexpf中
        // this.WOEXPF = project.getParam("WOEXPF") || "StringField1,StringField2,StringField3,StringField4,StringField5";
        this.WOEXPF = project.getParam("WOEXPF") || '';
        // 工单是否支持删除objective模式
        this.WODELO = project.getParam("WODELO");
        this.oldTaskBoundData = null;
        this.woStepGuid = '';
        this.dbPool = dbPool || '';
        this.subTasks = [];
        // 工单结束后是否继续当前实例而不是关闭后重新打开（如果工单对应动态页面一致的话，就可以减少重新绘制动态页面的时间）
        this.continuable = false;

        //TODO: 原来的插入回访记录，现在这里要怎么处理    创建objective 创建elitetask
        this.insertReplyCustomer = function (vObjTypeID, vCustID, vWOGUID) {
            var mObjType = this.getOneObjectiveInfo(vObjTypeID);
            if (mObjType["Flag"] == 1) {
                var data = project.commonQuerySync("ECore.Workorder.ObjCount", [vObjTypeID, vCustID, $E.staff.epidSql], this.dbPool);
                if (data && data.value[0].count <= 0) {
                    var mBDStr = $F.getDbTime();
                    var mEDStr = wo.getNextEndDate(mBDStr, mObjType["TypeID"], mObjType["Period"]);
                    this.insertOBRecord(Guid.raw(), vObjTypeID, mObjType["StaffID"], mObjType["GrpID"], mObjType["PriorityID"],
                        mEDStr, vCustID, $CONST.WO.WO_SysOpen, 3, 0, vWOGUID);
                }
            }
        };
        this.insertOBRecord = function (vObjID, vObjTypeID, vHandleByID, vHandleGroupID, vPriorityID, vExpriedDate, vCust_ID, vObjStatus, vTryTime, vSuccContactTime, vOldObjID) {
            var result = project.commonUpdateSync("ECore.Workorder.saveObj", [vObjID, vObjID, vObjTypeID, vHandleByID, vHandleGroupID, vPriorityID, vExpriedDate, vCust_ID, vObjStatus, vTryTime, vSuccContactTime, $E.staff.epidSql], this.dbPool)
            if (result > 0) {
                return 1;
            } else {
                return -1;
            }
        };

        this.getOneObjectiveInfo = function (vObjectTypeID) {
            var mReturnObj = {};
            var data = project.commonQuerySync("ECore.Workorder.OBJTypeInfo", [vObjectTypeID], this.dbPool);
            if (data && data.value[0].count > 0) {
                mReturnObj["Flag"] = 1;
                mReturnObj["PriorityID"] = data.value[0].fields[0][0];
                mReturnObj["Status"] = data.value[0].fields[0][1];
                mReturnObj["GrpID"] = data.value[0].fields[0][2];
                mReturnObj["StaffID"] = data.value[0].fields[0][3];
                mReturnObj["Period"] = data.value[0].fields[0][4];
                mReturnObj["TypeID"] = data.value[0].fields[0][5];
            } else {
                mReturnObj["Flag"] = 0;
            }
            return mReturnObj;
        };
    };

    Workorder.prototype = {
        cacheAllExtField : function (extFields) {
            extFields = extFields.trim();
            if ($F.isEmpty(extFields)) {
                return;
            }
            var extFieldsArray = extFields.split(",");
            extFieldsArray.forEach(extField => {
                if (!$F.isEmpty(extField)) {
                    this.allExtFields[extField.trim().toUpperCase()] = extField;
                    $F.log("Workorder3 - cacheAllExtField", "Exp Fields:" + extField);
                }
            });
        },
        /**
         * 加载一个objective，会根据是否可以重复来决定是加载老的还是创建新的
         */
        loadOneObjective : function (mapId, customerGuid, dupFlag) {
            const defaultCustomerGuid = this.project.getParam("CustDF");
            let objective;
            if ($F.equalsIgnoreCase(customerGuid, defaultCustomerGuid)) {
                objective = this.createNewObjective(mapId, customerGuid);
            } else {
                if (dupFlag > 0) {
                    objective = this.createNewObjective(mapId, customerGuid);
                } else {
                    objective = this.loadObjective(mapId, customerGuid);
                }
            }
            return objective;
        },

        /**
         * 创建新的objective对象
         * @param mapId
         * @param customerGuid
         * @param extTBName
         * @returns
         */
        createNewObjective : function (mapId, customerGuid, extTBName) {
            const objective = Objective.create(this.project, extTBName, true, this.dbPool);
            const woFrameMap = this.project.getFrameMap(mapId);
            objective.createData(woFrameMap.workorderFrameId, Guid.raw(), mapId);
            objective.modifyOneObjectiveValue("CUSTOMER_GUID", customerGuid);
            objective.boundData.setCreateInfo("CREATEDDATE", "CREATEDBY", $E.staff.id);
            objective.boundData.setModifyInfo("MODIFIEDDATE", "MODIFIEDBY", $E.staff.id);
            objective.flag = 1;
            return objective;
        },

        /**
         * 根据工单mapId和客户guid，加载objective
         */
        loadObjective : function (mapId, customerGuid, extTBName) {
            const objective = this.createNewObjective(mapId, customerGuid, extTBName);
            const ret = this.project.commonQuerySync("ECore.Objective.selectDetailByMapIdAndCustomer", [$E.staff.epidSql, mapId, customerGuid], this.dbPool);
            if (!$F.isNull(ret) && ret.value[0].count > 0) {
                for (let i = 0; i < ret.value[0].columns.length; i++) {
                    let f = ret.value[0].columns[i];
                    if (!$F.equalsIgnoreCase(f, "CREATEDBY") && !$F.equalsIgnoreCase(f, "MODIFIEDBY")) {
                        let v = ret.value[0].fields[0][i];
                        objective.setValue(f, v);
                    }
                }
                objective.flag = 0;
            }
            return objective;
        },

        /**
         * 根据oId加载objective
         * @param oId
         * @param extTBName
         * @returns {Object}
         */
        loadObjectiveByGuid : function (oId, extTBName) {
            const ret = this.project.commonQuerySync("ECore.Objective.selectDetailByGuid", [$E.staff.epidSql, oId], this.dbPool);
            if (!$F.isNull(ret) && ret.value[0].count > 0) {
                const objBean = $F.makeJsonBean(ret.value[0]);
                const objective = this.createNewObjective(objBean.MAP_ID, objBean.CUSTOMER_GUID, extTBName || '');
                for (let i = 0; i < ret.value[0].columns.length; i++) {
                    let f = ret.value[0].columns[i];
                    if (!$F.equalsIgnoreCase(f, "CREATEDBY") && !$F.equalsIgnoreCase(f, "MODIFIEDBY")) {
                        let v = ret.value[0].fields[0][i];
                        objective.setValue(f, v);
                    }
                }
                objective.flag = 0;
                return objective;
            }
        },

        /**
         * 根据tabc表查询objective相关信息
         * 仅当系统参数 WODELO 配置为1时候
         * @param oId
         * @param tabc
         */
        loadObjectiveByTabc: function(oId, tabc) {
            const ret = this.project.commonQuerySync("tabc.get", [tabc.toUpperCase(), $E.staff.epidSql, oId], this.dbPool);
            if (!$F.isNull(ret) && ret.value[0].count > 0) {
                const tabcBean = $F.makeJsonBean(ret.value[0]);
                const objective = this.createNewObjective(tabcBean.MAP_ID, tabcBean.CUSTOMER_GUID);
                objective.setValue("OBJECTIVE_GUID", oId);
                let objectiveFields = ['MAP_ID', 'CURSTEP_ID_MAP', 'OBJECTIVESUBSTATUS', 'OBJECTIVESTATUS', 'CUSTOMER_GUID'];
                objectiveFields.forEach(field => {
                    objective.setValue(field, tabcBean[field]);
                });
                objective.flag = 0;
                return objective;
            }
        },

        /**
         * 生成需要赋值给页面的变量键值对
         * @returns {{}}
         */
        generatePropertiesFields : function(){
            const wo = this;
            const currentStepId = wo.getCurStepIdMap();
            const curNode = wo.struct.getOneNode(currentStepId); // 根据当前步骤获取当前节点对象

            const fields = {};
            fields[WOUtils.PROPERTY.SAVE_STATUS] = {type: 0, data: wo.buttonStatus._SaveStatus == 1 ? 1 : 0};
            fields[WOUtils.PROPERTY.SEND_STATUS] = {type: 0, data: wo.buttonStatus._SendStatus == 1 ? 1 : 0};
            fields[WOUtils.PROPERTY.CLOSE_OR_REOPEN_STATUS] = {type: 0, data: wo.buttonStatus._CloseOrOpenStatus == 1 ? 1 : 0};
            fields[WOUtils.PROPERTY.END_STATUS] = {type: 0, data: wo.buttonStatus._EndStatus == 1 ? 1 : 0};
            fields[WOUtils.PROPERTY.REOPEN_STATUS] = {type: 0, data: wo.buttonStatus._ReOpenStatus == 1 ? 1 : 0};
            fields[WOUtils.PROPERTY.CANCEL_STATUS] = {type: 0, data: wo.buttonStatus._CancelStatus == 1 ? 1 : 0};
            fields[WOUtils.PROPERTY.FAILED_STATUS] = {type: 0, data: wo.buttonStatus._FailedStatus == 1 ? 1 : 0};
            fields[WOUtils.PROPERTY.TAKEOVER_STATUS] = {type: 0, data: wo.buttonStatus._TakeOverStatus == 1 ? 1 : 0};
            fields[WOUtils.PROPERTY.CD_STATUS] = {type: 0, data: wo.buttonStatus._CDStatus == 1 ? 1 : 0};
            fields[WOUtils.PROPERTY.ASSIST_STATUS] = {type: 0, data: wo.buttonStatus._AssistStatus == 1 ? 1 : 0};
            fields[WOUtils.PROPERTY.BACK_STATUS] = {type: 0, data: wo.buttonStatus._BackStatus == 1 ? 1 : 0};
            fields[WOUtils.PROPERTY.REPLY_STATUS] = {type: 0, data: wo.buttonStatus._ReplyCustomerStatus == 1 ? 1 : 0};
            fields[WOUtils.PROPERTY.TRANSFER_STATUS] = {type: 0, data: wo.buttonStatus.transferStatus == 1 ? 1 : 0};
            fields[WOUtils.PROPERTY.REVOKE_STATUS] = {type: 0, data: wo.buttonStatus.revokeStatus == 1 ? 1 : 0};
            fields[WOUtils.PROPERTY.CC_READ_STATUS] = {type: 0, data: wo.buttonStatus.ccReadStatus == 1 ? 1 : 0};

            fields[WOUtils.PROPERTY.CLOSE_FLAG] = {type: 0, data: wo.buttonStatus._CloseFlag};
            fields[WOUtils.PROPERTY.STEP_COMMENTS] = {type: 0, data: wo.getCurStepComments()};
            fields[WOUtils.PROPERTY.CURRENT_HANDLE_BY] = {type: 0, data: wo.getHandleById()};
            fields[WOUtils.PROPERTY.CURRENT_HANDLE_GRP] = {type: 0, data: wo.getHandleGroupId()};
            fields[WOUtils.PROPERTY.FRAME_ID] = {type: 0, data: wo.getWorkOrderFrameId()};
            fields[WOUtils.PROPERTY.PRIORITY] = {type: 0, data: wo.getPriorityId()};
            fields[WOUtils.PROPERTY.NEXT_STEP_BEGIN_DATE] = {type: 0, data: wo.getExpectStartTime()};
            fields[WOUtils.PROPERTY.NEXT_STEP_EXPIRE_DATE] = {type: 0, data: wo.getExpiredDate()};
            fields[WOUtils.PROPERTY.FORECAST_DATE] = {type: 0, data: wo.getForecastDate()};
            fields[WOUtils.PROPERTY.STEP_FORECAST_DATE] = {type: 0, data: wo.getStepForecastDate()};
            fields[WOUtils.PROPERTY.STATUS] = {type: 0, data: wo.getWorkOrderStatus()};
            fields[WOUtils.PROPERTY.STEP_ID] = {type: 0, data: wo.getCurStepIdMap()};
            fields[WOUtils.PROPERTY.STEP_STATUS] = {type: 0, data: wo.getCurStepStatus()};
            fields[WOUtils.PROPERTY.NEW_FLAG] = {type: 0, data: wo.newFlag ? 1 : 0};

            fields[WOUtils.PROPERTY.FRAME_DESC] = {type: 0, data: wo.struct.getStruct().MAPNAME};
            fields[WOUtils.PROPERTY.STEP_DESC] = {type: 0, data: curNode.NODEMAPNAME};
            fields[WOUtils.PROPERTY.TASK_GUID] = {type: 0, data: wo.getTaskGuid()};
            fields[WOUtils.PROPERTY.TASK_CREATED_DATE] = {type: $CONST.BoundType.F_Date, data: wo.getCurStepCreatedDate()};
            fields[WOUtils.PROPERTY.CREATED_DATE] = {type: $CONST.BoundType.F_Date, data: wo.getCreatedDate()};
            fields[WOUtils.PROPERTY.NODE_TEMPLATE] = {type: 0, data: curNode.DOCTEMPLATE};
            fields[WOUtils.PROPERTY.NODE_MULTI_TASK] = {type: 0, data: curNode.MULTITASK};
            fields[WOUtils.PROPERTY.NODE_TASK_FLAG] = {type: 0, data: curNode.TASKFLAG};

            for (let i = 0, expfarr = this.WOEXPF.split(","); i < expfarr.length; i++) {
                let expf = expfarr[i].trim();
                if (!$F.isEmpty(expf)) {
                    let mTmpStr = "TEMP.WORKORDER" + expf;
                    fields[mTmpStr] = {type: 0, data: wo.woBoundData.getValue(expf)};
                }
            }
            return fields;
        },

        /**
         * 1.load by objective_guid
         * 根据oId打开工单
         * @param params
         *  objectiveGuid
         *  taskId
         *  addinId
         *  tabName
         *  parameters
         *  notifyId
         *  container
         *  readonly
         *  continuable 是否可持续的在一个instance中打开
         *  lastInstance 之前的instance
         *  forceProxy 强制代理模式
         *  tabc 从哪个tabc表读取信息（当objective表数据删除了，且WODELO配置启动时候）
         * @returns {number}
         */
        loadByObjectiveGuid : function (params) {
            let { objectiveGuid, taskId, addinId, tabName, parameters, notifyId, container, readonly, continuable, lastInstance, forceProxy, tabc } = params;
            const wo = this, project = this.project;
            this.continuable = !!continuable;
            let objective = this.loadObjectiveByGuid(objectiveGuid);
            if (!objective) {
                // objective表没有查询到相关数据时候
                if (this.WODELO && tabc) {
                    // 根据tabc表查询objective相关字段信息
                    objective = this.loadObjectiveByTabc(objectiveGuid, tabc);
                    readonly = true; //这时候 强制只读模式
                }
                if (!objective) {
                    $F.err('Workorder3 - loadByObjectiveGuid', $F.lang('wo.noObjectiveFoundAlert') + ' : ' + objectiveGuid);
                    return -1;
                }
            }

            const mapId = objective.getValue("MAP_ID");
            wo.customer_guid = objective.getValue("customer_guid");
            wo.forceProxy = forceProxy;
            if (lastInstance && lastInstance.workorder) { // 仅当工单
                if (mapId === lastInstance.workorder.getFrameMapId()) { // 如果mapId相同，则可以利用原来的工单instance
                    wo.struct = lastInstance.workorder.struct;
                    return wo.loadWorkorder({
                        objectiveGuid : objectiveGuid,
                        taskId : taskId,
                        objective : objective,
                        addinId : addinId,
                        tabName : tabName,
                        parameters : parameters,
                        notifyId : notifyId,
                        container : container,
                        readonly: readonly,
                        lastInstance: lastInstance
                    });
                }
            }
            const woFrameMap = project.getFrameMap(mapId);
            if (!woFrameMap) {
                $F.err('Workorder3 - loadByObjectiveGuid', 'No workorder found with woFrameMapId: ' + mapId);
                return -1;
            }
            wo.struct = Struct.fetchStruct(project, mapId);
            if ($F.isEmpty(wo.struct)) {
                $F.err('Workorder3 - loadByObjectiveGuid', $F.lang('wo.noStructFoundAlert') + ' : ' + objectiveGuid);
                return -1;
            }
            return wo.loadWorkorder({
                objectiveGuid : objectiveGuid,
                taskId : taskId,
                objective : objective,
                addinId : addinId,
                tabName : tabName,
                parameters : parameters,
                notifyId : notifyId,
                container : container,
                readonly: readonly
            });
        },

        /**
         * 2.load by map_id
         * 根据otId打开工单，可能是新建工单(其实是根据mapId打开)
         *
         * @param params
         * 	mapId
         *  dupFlag
         *  addinId
         *  tabName
         *  parameters
         *  container
         *  customerGuid 可以指定某个客户
         *  stepId 指定某个步骤
         *  readonly 只读模式
         *  rolegroupId 子流程id(机构id)
         * @returns {number}
         */
        loadByOtId : function (params) {
            const { mapId, dupFlag, addinId, tabName, parameters, container, customerGuid, stepId, readonly, alwaysNew, rolegroupId } = params;
            const wo = this, project = this.project;
            const woFrameMap = project.getFrameMap(mapId);
            if (!woFrameMap) {
                $F.err('Workorder3 - loadByOtId', 'No workorder found with woFrameMapId: ' + mapId);
                return -1;
            }
            wo.setFrameMapId(mapId);
            wo.struct = Struct.fetchStruct(project, mapId);
            if ($F.isEmpty(wo.struct)) {
                $F.err('Workorder3 - loadByOtId', $F.lang('wo.noStructFoundAlert') + ' : ' + mapId);
                return -1;
            }

            const allStruct = wo.struct.getStruct();
            let mCustomer_GUID = allStruct.CUSTOMER_GUID;
            const mCustomerFlag = $F.parseInt(allStruct.CUSTOMERFLAG);
            $F.log('WorkOrder3 - loadByOtId', 'Wo change customer: ' + mCustomer_GUID + ' flag:' + mCustomerFlag);

            var mUserCustomer_GUID;
            if (customerGuid) {
                mUserCustomer_GUID = customerGuid;
            } else {
                var currentCustomerGuid = project.getCustomerGuid();
                if (!$F.isEmpty(mCustomer_GUID)) {
                    while (mCustomer_GUID.length < 36) {
                        mCustomer_GUID = mCustomer_GUID + " ";
                    }
                }
                // 允许工单客户是个空字符串
                if (mCustomerFlag === 0) { // 使用框架客户
                    if ($F.isEmpty(currentCustomerGuid)) {
                        $F.alert($F.lang('wo.noCurrentCustomerAlert'));
                        return -1;
                    }
                    mUserCustomer_GUID = currentCustomerGuid;
                } else if (mCustomerFlag === 1) { // 使用工单配置默认客户
                    mUserCustomer_GUID = mCustomer_GUID;
                    $F.log('WorkOrder3 - loadByOtId', 'Use workorder default customer : ' + mCustomer_GUID);
                } else { // 如果无框架客户，则用工单默认客户
                    if ($F.isEmpty(currentCustomerGuid)) {
                        mUserCustomer_GUID = mCustomer_GUID;
                        $F.log('WorkOrder3 - loadByOtId', 'Current customer is null, use workorder default customer : ' + mCustomer_GUID);
                    } else {
                        mUserCustomer_GUID = currentCustomerGuid;
                    }
                }
            }
            $F.log('WorkOrder3 - loadByOtId', 'Current Customer GUID : ' + mUserCustomer_GUID);
            wo.customer_guid = mUserCustomer_GUID;

            const woOpened = this.isWOOpenedByMapId({
                otId: mapId,
                dupFlag: dupFlag,
                customerGuid: mUserCustomer_GUID,
                alwaysNew: alwaysNew
            });
            if (woOpened && woOpened.opened) {
                project.tabs.xtabs({
                    active : woOpened.index
                });
                return;
            }

            const objective = wo.loadOneObjective(mapId, mUserCustomer_GUID, dupFlag);
            const objectiveGuid = objective.boundData.getValue("objective_guid");
            let taskId = '';
            if (objective.flag == 0) {//发现存在已有open的objective
                const ret = project.commonQuerySync("ECore.Workorder.selectEliteTaskByOId", [objectiveGuid], this.dbPool);
                if (!$F.isNull(ret) && ret.value[0].count > 0) {
                    let eliteTasks = $F.makeJsonArray(ret.value[0]);
                    let findTaskId;
                    $.each(eliteTasks, function (i, task) {
                        if (task.HANDLEBY_ID === $E.staff.id) {
                            findTaskId = task.ELITETASK_GUID;
                            return false;
                        }
                    });
                    if (!findTaskId) {
                        taskId = eliteTasks[0].ELITETASK_GUID;
                    } else {
                        taskId = findTaskId;
                    }
                }
                if (!taskId) {
                    return $F.alert($F.lang('wo.noTaskFoundAlert'));
                }
            }
            return wo.loadWorkorder({
                objectiveGuid : objectiveGuid,
                taskId : taskId,
                objective : objective,
                addinId : addinId,
                tabName : tabName,
                parameters : parameters,
                container : container,
                stepId : stepId,
                readonly: readonly,
                rolegroupId
            });
        },

        /**
         * 判断工单是不是已经被打开
         */
        isWOOpenedByMapId : function(params) {
            if (params.alwaysNew) {
                return;
            }
            var otId = params.otId,
                dupFlag = params.dupFlag,
                findOId = '',
                customerGuid = params.customerGuid,
                sqlKey = 'ECore.Objective.selectDetailByMapIdAndCustomer';
            var ret = this.project.commonQuerySync(sqlKey, [ $E.staff.epidSql, otId, customerGuid ], this.dbPool);
            if (!$F.isNull(ret) && ret.value[0].count > 0) {
                findOId = ret.value[0].fields[0][0];
            }
            var ts = this.project.getNavs();
            for (var i = 0; i < ts.length; i++) {
                var $this = $(ts[i]);
                var instance = $this.data("instance");
                if ($this.data("otId") === otId) {
                    //判断instance里的objective是否新创建的，如果是，则不需要新打开，直接打开之前的
                    //不需要判断instance.objective上的customer_guid是否是当前customer.customer_guid, 因为客户改变的话应该发出消息通知各模块，工单收到改变消息后会修改instance.objective上的customer_guid
                    if (instance.flag) {
                        return {
                            opened : true,
                            index : i,
                            instance : instance
                        };
                    } else {
                        if (dupFlag != 1) {
                            if (instance.oId === findOId) {
                                return {
                                    opened : true,
                                    index : i,
                                    instance : instance
                                };
                            }
                        }
                    }
                }

                if (instance.wos) {
                    for (var j = 0; j < instance.wos.length; j++) {
                        var wo = instance.wos[j];
                        if (wo.getFrameMapId() === otId) {
                            if (wo.newFlag == 1) { //新建工单
                                return {
                                    opened : true,
                                    index : i
                                };
                            }
                            if (dupFlag != 1) {
                                if (wo.guid === findOId) {
                                    return {
                                        opened : true,
                                        index : i,
                                        instance : instance
                                    };
                                }
                            }
                        }
                    }
                }
            }
        },

        /**
         * 加载工单信息，并显示
         * @param params
         * @returns {instance}
         */
        loadWorkorder : function (params) {
            let workorderGuid = params.objectiveGuid,
                taskId = params.taskId,
                objective = params.objective,
                addinId = params.addinId,
                tabName = params.tabName,
                parameters = params.parameters,
                notifyId = params.notifyId,
                container = params.container,
                stepId = params.stepId,
                readonly = params.readonly,
                rolegroupId = params.rolegroupId,
                lastInstance = params.lastInstance;

            const wo = this, project = this.project;
            wo.objective = objective;
            wo.addinId = addinId;
            let newFlag = wo.loadMeData({
                workorderGuid,
                taskId,
                rolegroupId,
                objective
            });
            if (newFlag === -1) {// 创建新工单加载数据失败
                $F.log('Workorder3 - loadWorkorder', 'Error occurred on loading data');
                return;
            }
            taskId = wo.getTaskGuid();
            wo.taskId = taskId;
            wo.notifyId = notifyId;
            wo.newFlag = newFlag;
            wo.woStepGuid = Guid.raw();
            wo.readonly = !!readonly;

            let currentStepId = wo.getCurStepIdMap();
            if (stepId) {
                currentStepId = stepId;
                wo.setCurStepIdMap(stepId);
                $F.log('Workorder3 - loadWorkorder', 'Load workorder with specified stepId: ' + stepId);
            }

            if (newFlag == 1) {//new wo
                wo.oldFrameId = "";
                wo.oldStepIdMap = "";
            } else {
                wo.oldFrameId = wo.getWorkOrderFrameId();
                wo.oldStepIdMap = currentStepId;
            }
            wo.frameId = wo.getWorkOrderFrameId();
            wo.curStepIdMap = currentStepId;

            const curStruct = wo.struct.getStruct();
            let currentNode = wo.struct.getOneNode(currentStepId);
            if (!currentNode) {
                $F.log("Workorder3 - loadWorkorder", "None current node found by stepId " + currentStepId);
                return;
            }
            if (currentNode.his) {
                wo.readonly = true;
                $F.alert($F.lang('wo.nodeDeletedAlert'));
            }

            if (!wo.isMobile) {
                // 绘制流程的导航
                let simpleFlow = true, nodes = wo.struct.getAllNodes(), links = wo.struct.getAllLinks(), firstNode, sourceNode, targetNode;
                $.each(nodes, function (k, node) {
                    node.nextNode = undefined;
                });

                // 获取第一个节点
                const firstStepId = wo.struct.getFirstNode({
                    rolegroupId: wo.getRolegroupId()
                });
                firstNode = wo.struct.getOneNode(firstStepId);
                let targetIdMap = {};
                $.each(links, function (k, link) {
                    if (link.SOURCE_ID !== 'BEGIN') {
                        sourceNode = WOUtils.findNode(nodes, link.SOURCE_ID);
                        if (sourceNode) {
                            if (sourceNode.nextNode) {
                                //$F.log('Workload - loadWorkorder', '多链接流程，不支持简单绘制', sourceNode);
                                simpleFlow = false;
                                return false;
                            }
                            if (link.TARGET_ID !== 'END') {
                                targetNode = WOUtils.findNode(nodes, link.TARGET_ID);
                                sourceNode.nextNode = targetNode;
                            }
                        }
                    }
                    // 判断是否有多个链路的终点指向同一个节点，如果有，则也不认为是简单流程（包含子任务的时候就会有这种情况）
                    if (targetIdMap[link.TARGET_ID]) {
                        simpleFlow = false;
                        return false;
                    } else {
                        targetIdMap[link.TARGET_ID] = 1;
                    }
                });

                //简单流程
                if (simpleFlow) {
                    if (!firstNode)
                        $F.err('Workload - loadWorkorder', $F.lang('wo.cannotFoundEntryNode'));
                    else {
                        const flows = [{
                            NODEMAP_ID : firstNode.NODEMAP_ID,
                            NodeName : firstNode.NODEMAPNAME
                        }];

                        while (firstNode.nextNode) {
                            const contains = flows.contains(firstNode.nextNode, function(flow, nextNode) {
                                return flow.NODEMAP_ID === nextNode.NODEMAP_ID;
                            });
                            if (contains) {
                                $F.log('Workload - loadWorkorder', $F.lang('wo.loopNodeFound') + firstNode.nextNode.NODEMAPNAME);
                                break;
                            }
                            firstNode.woGuid = workorderGuid;
                            flows.push({
                                NODEMAP_ID : firstNode.nextNode.NODEMAP_ID,
                                NodeName : firstNode.nextNode.NODEMAPNAME
                            });
                            firstNode = firstNode.nextNode;
                        }
                        let processed = true;
                        $.each(flows, function () {
                            this.processed = processed;
                            if (wo.curStepIdMap === this.NODEMAP_ID)
                                processed = false;
                        });
                        wo.struct.WO_SIMPLE_FLOWS = flows;
                    }
                }
                //复杂流程的话，就显示历史步骤
                else {
                    const flows = [];
                    //select workorderstep_guid,from_stepidmap,oldhandleby_id,oldhandlegroup_id,handleby_id,handlegroup_id,workorderstepstatus,comments from WorkOrderStep2{C1} where WorkOrder_GUID=@{C2}S order by modifieddate
                    const data = project.commonQuerySync("ECore.Workorder.selectWOTrace3", [workorderGuid], this.dbPool);
                    if (data && data.value[0].count > 0) {
                        for (let i = 0; i < data.value[0].count; i++) {
                            let stepGuid = data.value[0].fields[i][0];
                            let fromStepId = data.value[0].fields[i][1];
                            let node = wo.struct.getOneNode(fromStepId);
                            if (node) {
                                let flow = Object.assign({}, node);
                                flow.woGuid = workorderGuid;
                                flow.stepGuid = stepGuid;
                                flow.processed = true;
                                flow.NodeName = node.NODEMAPNAME;
                                flows.push(flow);
                            }
                        }
                    }
                    let currentNode = wo.struct.getOneNode(currentStepId);
                    if (currentNode) {
                        currentNode.woGuid = workorderGuid;
                        currentNode.processed = true;
                        currentNode.NodeName = currentNode.NODEMAPNAME;
                        flows.push(currentNode);
                    }
                    wo.struct.WO_SIMPLE_FLOWS = flows;
                }
                // end
            }

            if (!wo.isMobile) {
                if (currentNode.REAL_GROUP_IDA) {
                    wo.showBuildId = currentNode.REAL_GROUP_IDA;
                } else if (curStruct.REAL_GROUP_IDA) {
                    wo.showBuildId = curStruct.REAL_GROUP_IDA;
                } else {
                    $F.log("Workorder3 - loadWorkorder", "No GroupIDA found on currentNode " + currentStepId);
                    return;
                }
            } else {
                if (currentNode.REAL_GROUPIDAMOBILE)
                    wo.showBuildId = currentNode.REAL_GROUPIDAMOBILE;
                else if (currentNode.REAL_GROUP_IDA)
                    wo.showBuildId = currentNode.REAL_GROUP_IDA;
                else {
                    if (curStruct.REAL_GROUPIDAMOBILE)
                        wo.showBuildId = curStruct.REAL_GROUPIDAMOBILE;
                    else
                        wo.showBuildId = curStruct.REAL_GROUP_IDA;
                }
            }
            $F.log('Workorder3 - loadWorkorder', 'VESUPPORT is ' + curStruct.VESUPPORT);

            let instance;
            if (lastInstance && lastInstance.workorder.showBuildId === wo.showBuildId) {
                lastInstance.attributes[$CONST.Instance.ON_DESTROY] = [];
                delete lastInstance.externalListener['WORKORDER_' + lastInstance.workorder.guid];
                wo.container = lastInstance.workorder.container;
                wo.showOnTab = lastInstance.workorder.showOnTab;
                instance = lastInstance;
                instance.parameters = parameters;
            } else {
                if (lastInstance) {
                    // 如果buildId不同，则关闭之前的工单，从新打开新的
                    lastInstance.workorder.continuable = false;
                    lastInstance.workorder.clearInfo();
                }
                instance = $D.createInstance(project, wo.showBuildId, '', '', parameters, true, "WO", curStruct.VESUPPORT == 1);
            }
            if (instance) {
                instance.onDestroy(function () {
                    project.doUnlock(wo.genLockKey(), '', wo.dbPool);
                });
                $F.log("WorkOrder3 - loadWorkorder", "Start to register event listener to workorder[" + workorderGuid + "] instance.");
                wo.instance = instance;
                wo.instance.workorder = wo;
                wo.tabName = tabName || '工单';

                // 加载链路相关信息
                wo.nextStepInfo.buildNextSteps();
                // 设置操作按钮状态
                wo.setButtonStatus();

                const fields = wo.generatePropertiesFields();
                instance.batchFireValue(fields, true);

                instance.propertyListener('WORKORDER_' + workorderGuid, function (pName, pValue) {
                    let mStrPropertyName = pName.toUpperCase();
                    if (!wo || !wo.buttonStatus)
                        return;
                    if (wo.buttonStatus.getCloseFlag() == 1 &&
                        mStrPropertyName !== WOUtils.PROPERTY.CANCEL.toUpperCase() &&
                        mStrPropertyName !== WOUtils.PROPERTY.CD.toUpperCase() &&
                        mStrPropertyName !== WOUtils.PROPERTY.ASSIST.toUpperCase() &&
                        mStrPropertyName !== WOUtils.PROPERTY.STEP_COMMENTS.toUpperCase() &&
                        mStrPropertyName !== WOUtils.PROPERTY.CLOSE_OR_REOPEN.toUpperCase() &&
                        mStrPropertyName !== WOUtils.PROPERTY.END.toUpperCase() &&
                        mStrPropertyName !== WOUtils.PROPERTY.REOPEN.toUpperCase() &&
                        mStrPropertyName !== WOUtils.PROPERTY.TAKEOVER.toUpperCase()) {
                        return;
                    }

                    if ($F.equalsIgnoreCase(mStrPropertyName, WOUtils.PROPERTY.TAB_CLOSE)) {
                        if (pValue == 1) {
                            $F.fireValue(instance,  WOUtils.PROPERTY.TAB_CLOSE, "0");
                            $F.log('workorder - propertyChange', "WOClear Result:" + wo.clearInfo());
                        }
                    } else if ($F.equalsIgnoreCase(mStrPropertyName, WOUtils.PROPERTY.STEP_COMMENTS)) {
                        wo.setCurStepComments(pValue);
                    } else if ($F.equalsIgnoreCase(mStrPropertyName, WOUtils.PROPERTY.SAVE)) {
                        if (pValue == 1) {
                            $F.fireValue(instance,  WOUtils.PROPERTY.SAVE, "0");
                            $F.log('workorder - propertyChange', "WOSave Result: " + wo.save());
                        } else if (pValue == 2) {
                            $F.fireValue(instance,  WOUtils.PROPERTY.SAVE, "0");
                            $F.log('workorder - propertyChange', "WOSave 0 Result: " + wo.save(0));
                        }
                    } else if ($F.equalsIgnoreCase(mStrPropertyName, WOUtils.PROPERTY.SEND)) {
                        if (pValue == 1) {
                            $F.fireValue(instance,  WOUtils.PROPERTY.SEND, "0");
                            $F.log('workorder - propertyChange', "WOSend Flag is 0 Result:" + wo.send(0));
                        } else if (pValue == 2) {
                            $F.fireValue(instance,  WOUtils.PROPERTY.SEND, "0");
                            $F.log('workorder - propertyChange', "WOSend Flag is 1 Result:" + wo.send(1));
                        }
                    } else if ($F.equalsIgnoreCase(mStrPropertyName, WOUtils.PROPERTY.CLOSE_OR_REOPEN)) {
                        if (pValue == 1) {
                            $F.fireValue(instance,  WOUtils.PROPERTY.CLOSE_OR_REOPEN, "0");
                            $F.log('workorder - propertyChange', "WOClose Result:" + wo.close());
                        }
                    } else if ($F.equalsIgnoreCase(mStrPropertyName, WOUtils.PROPERTY.END)) {
                        if (pValue == 1) {
                            $F.fireValue(instance,  WOUtils.PROPERTY.END, "0");
                            $F.log('workorder - propertyChange', "WOClose Result:" + wo.closeAndReopen());
                        }
                    } else if ($F.equalsIgnoreCase(mStrPropertyName, WOUtils.PROPERTY.REOPEN)) {
                        if (pValue == 1) {
                            $F.fireValue(instance,  WOUtils.PROPERTY.REOPEN, "0");
                            $F.log('workorder - propertyChange', "WOClose Result:" + wo.closeAndReopen());
                        }
                    } else if ($F.equalsIgnoreCase(mStrPropertyName, WOUtils.PROPERTY.CANCEL)) {
                        if (pValue == 1) {
                            $F.fireValue(instance,  WOUtils.PROPERTY.CANCEL, "0");
                            $F.log('workorder - propertyChange', "WOCancel Result:" + wo.cancel());
                        }
                    } else if ($F.equalsIgnoreCase(mStrPropertyName, WOUtils.PROPERTY.FAILED)) {
                        if (pValue == 1) {
                            $F.fireValue(instance,  WOUtils.PROPERTY.FAILED, "0");
                            $F.log('workorder - propertyChange', "WOfailed Result:" + wo.failedClose());
                        }
                    } else if ($F.equalsIgnoreCase(mStrPropertyName, WOUtils.PROPERTY.TAKEOVER)) {
                        if (pValue == 1) {
                            $F.fireValue(instance,  WOUtils.PROPERTY.TAKEOVER, "0");
                            $F.log('workorder - propertyChange', "WOTakeOver Result:" + wo.takeOver());
                        }
                    } else if ($F.equalsIgnoreCase(mStrPropertyName, WOUtils.PROPERTY.CD)) {
                        if (pValue == 1) {
                            $F.fireValue(instance,  WOUtils.PROPERTY.CD, "0");
                            $F.log('workorder - propertyChange', "WOCD Result:" + wo.cdInfo());
                        } else if (pValue == 2) {//2的时候增加tabc表的保存
                            $F.fireValue(instance,  WOUtils.PROPERTY.CD, "0");
                            $F.log('workorder - propertyChange', "WOCD Result:" + wo.cdInfo(2));
                        }
                    } else if ($F.equalsIgnoreCase(mStrPropertyName, WOUtils.PROPERTY.ASSIST)) {
                        if (pValue == 1) {
                            $F.fireValue(instance,  WOUtils.PROPERTY.ASSIST, "0");
                            $F.log('workorder - propertyChange', "WOAssist Result:" + wo.assist());
                        }
                    } else if ($F.equalsIgnoreCase(mStrPropertyName, WOUtils.PROPERTY.BACK)) {
                        if (pValue == 1) {
                            $F.fireValue(instance,  WOUtils.PROPERTY.BACK, "0");
                            $F.log('workorder - propertyChange', "WOBack Result:" + wo.back());
                        }
                    } else if ($F.equalsIgnoreCase(mStrPropertyName, WOUtils.PROPERTY.REVOKE)) {
                        if (pValue == 1) {
                            $F.fireValue(instance,  WOUtils.PROPERTY.REVOKE, "0");
                            $F.log('workorder - propertyChange', "WORevoke Result:" + wo.revoke());
                        }
                    } else if ($F.equalsIgnoreCase(mStrPropertyName, WOUtils.PROPERTY.CC_READ)) {
                        if (pValue == 1) {
                            $F.fireValue(instance,  WOUtils.PROPERTY.CC_READ, "0");
                            $F.log('workorder - propertyChange', "WOCCRead Result:" + wo.ccRead());
                        }
                    } else if ($F.equalsIgnoreCase(mStrPropertyName, WOUtils.PROPERTY.PRIORITY)) {
                        wo.setPriorityId(pValue);
                    } else if ($F.equalsIgnoreCase(mStrPropertyName, WOUtils.PROPERTY.EXPIRE_DATE)) {
                        if ($F.isDate(pValue)) {
                            wo.setExpiredDate(pValue);
                        }
                    } else if ($F.equalsIgnoreCase(mStrPropertyName, WOUtils.PROPERTY.FORECAST_DATE)) {
                        if ($F.isDate(pValue)) {
                            wo.setForecastDate(pValue);
                        }
                    } else if ($F.equalsIgnoreCase(mStrPropertyName, WOUtils.PROPERTY.STEP_FORECAST_DATE)) {
                        if ($F.isDate(pValue)) {
                            wo.setStepForecastDate(pValue);
                        }
                    } else if ($F.equalsIgnoreCase(mStrPropertyName, WOUtils.PROPERTY.NEXT_STEP_BEGIN_DATE)) {
                        if ($F.isDate(pValue)) {
                            wo.setExpectStartTime(pValue);
                        }
                    } else if ($F.equalsIgnoreCase(mStrPropertyName, WOUtils.PROPERTY.TRANSFER)) {
                        if (pValue == 1) {
                            $F.fireValue(instance,  WOUtils.PROPERTY.TRANSFER, "0");
                            $F.log('workorder - propertyChange', "WOTransfer Result:" + wo.transfer());
                        }
                    } else if ($F.equalsIgnoreCase(mStrPropertyName, WOUtils.PROPERTY.OPERATE_TASK_GUID)) {
                        wo.operateTaskGuid = pValue;
                    } else if ($F.equalsIgnoreCase(mStrPropertyName, WOUtils.PROPERTY.REVOKE_TO_NODEMAP_ID)) {
                        wo.revokeToNodeMapId = pValue;
                    } else if ($F.equalsIgnoreCase(mStrPropertyName, WOUtils.PROPERTY.OBJECTIVE_STATUS)) {
                        wo.setObjectiveStatus(pValue);
                    } else if ($F.equalsIgnoreCase(mStrPropertyName, WOUtils.PROPERTY.ROLEGROUP_ID)) {
                        wo.setRolegroupId(pValue);
                    } else if ($F.equalsIgnoreCase(mStrPropertyName, WOUtils.PROPERTY.SUB_STATUS)) {
                        wo.setObjectiveSubStatus(pValue);
                        wo.fireButtonStatus();
                    } else {
                        if (mStrPropertyName.substr(0, 14) === "TEMP.WORKORDER") {
                            wo.setExtFieldValue(mStrPropertyName.substr(14, mStrPropertyName.length - 14), pValue);
                        } else if (mStrPropertyName.substr(0, 12) === "TEMP.WOSTEP_") {
                            wo.setSendStepInfo(mStrPropertyName.substr(12, mStrPropertyName.length - 12), pValue);
                        }
                    }
                });

                if (objective) {
                    instance.flag = (objective.flag === 1);
                    objective.setValue('map_id', wo.getFrameMapId());
                    objective.setValue('workorderframe_id', wo.getWorkOrderFrameId());
                    objective.setValue('curstep_id_map', wo.getCurStepIdMap());
                }

                wo.instance.options = {
                    woTabc: wo.getCurrentMapExtends('woTabc')
                };

                //show workorder
                if (!wo.isMobile) {
                    if (wo.struct.getStruct().SHOWTRACEFLAG == 1) {
                        wo.instance.WO_SIMPLE_FLOWS = wo.struct.WO_SIMPLE_FLOWS;
                    }

                    if (params.lastInstance) { // 不从新打开一个新的容器，而是从之前实例中获取容器
                        wo.instance.init(objective, true);
                        // 刷新temp和tabc数据
                        Object.keys(instance.data).forEach(key => {
                            if (key !== 'temp') {
                                for (let runtimeKey in instance.databinder.runtimes) {
                                    let runtime = instance.databinder.runtimes[runtimeKey];
                                    if (runtime.data.local && runtime.data.local.tabc) {
                                        const fields = {};
                                        Object.keys(instance.data[key]).forEach(dataKey => {
                                            fields['tabc.' + dataKey] = {
                                                type: 0,
                                                data: instance.data[key][dataKey]
                                            };
                                        });
                                        instance.batchFireCtl(fields, runtime);
                                    }
                                    if (instance.runtime.id === runtime.id) {
                                        // pub的数据，只需要batchFireCtl一次，不需要循环runtime去batchFire
                                        if (runtime.data.pub && runtime.data.pub[key]) {
                                            const fields = {};
                                            Object.keys(instance.data[key]).forEach(dataKey => {
                                                fields[key + '.' + dataKey] = {
                                                    type: 0,
                                                    data: instance.data[key][dataKey]
                                                };
                                            });
                                            instance.batchFireCtl(fields, runtime);
                                        }
                                    }
                                }
                            } else {
                                const fields = {};
                                const woPropMap = {};
                                Object.keys(WOUtils.PROPERTY).forEach(woPropKey => {
                                    woPropMap[WOUtils.PROPERTY[woPropKey].toLowerCase()] = 1;
                                });
                                Object.keys(instance.data[key]).forEach(dataKey => {
                                    let propName = ('temp.' + dataKey).toLowerCase();
                                    if (!woPropMap[propName]) { // 工单定义的变量就不需要重复刷新了
                                        fields[propName] = {
                                            type: 0,
                                            data: instance.data[key][dataKey]
                                        };
                                    }
                                });
                                instance.batchFireCtl(fields, instance.runtime);
                            }
                        });
                        // 触发instance重置命令
                        wo.instance.active({
                            objectiveGuid: wo.getObjectiveGuid(),
                            taskGuid: wo.getTaskGuid()
                        });
                    } else {
                        if (!container) {
                            const tab = project.tabCreate(workorderGuid.trim() + '-' + taskId.trim() + (notifyId || ''), wo.tabName, true);
                            tab.data('instance', wo.instance)
                                .data('addinId', wo.addinId.toUpperCase())
                                .data('otId', wo.getFrameMapId())
                                .data('taskId', taskId).data('notifyId', notifyId);
                            container = tab.data("container");
                            wo.showOnTab = true;
                        } else {
                            container.empty();
                        }
                        wo.container = container;
                        $D.live(wo.instance, container, objective);
                    }

                    wo.instance.objective.taskId = taskId;
                    wo.instance.objective.taskBoundData = wo.taskBoundData;
                } else {
                    project.openDyn(wo.tabName, wo.instance, objective);
                    if (wo.instance.objective) {
                        wo.instance.objective.taskId = taskId;
                        wo.instance.objective.taskBoundData = wo.taskBoundData;
                    }
                }

                //project.addModuleInstance($CONST.ModuleName.ADDIN_WORKORDER, wo.instance);
//                project.events.register($CONST.ModuleEvent.ECM_CUSTOMERCHANGE, wo.instance.id, function (p, ev) {
//                    var w = ev.w;
//                    if (w.instance.flag) {//只有新建工单时候，当前客户改变需要同时去修改instance.objective上的值
//                        w.setCustomerGuid(project.getCustomerGuid());
//                        //可能打开工单时候，由于工单配置上的获取到的默认打开工单客户和当前客户不是同一个，当客户改变时候，就认为使用者想把当前框架客户赋值到当前工单上
//                        w.instance.customer = project.customer;
//                    }
//                }, {w: wo});
                if (wo.instance.struct.version !== 'DYN2') {
                    //新版本动态页面没有-99了，都在active命令里了
                    wo.instance.doCmdIncludeComponents("P-99");
                }
            }

            // 在工单动态页面加载完后，在判断加锁状态，提示成功失败
            if (wo.lockRet) {
                if (wo.lockRet.code > 0) {
                    $F.notify($F.lang('sys.lockSuccess'));
                } else {
                    if (wo.lockRet.code == -2) {
                        let WOLALT = this.project.getParam("WOLALT");
                        if (wo.lockRet.message === $E.staff.id) {
                            let message = $F.ognl($F.lang('wo.lockedBySelfAlert'), function() {
                                return $F.parseClientFlag(wo.lockRet.value);
                            });
                            if (WOLALT === 'alert') {
                                $F.alert(message);
                            } else {
                                $F.notify(message, 0, undefined, undefined, 3000);
                            }
                        } else {
                            const staff = project.getStaffById(wo.lockRet.message);
                            let message = $F.ognl($F.lang('wo.lockedAlert'), function() {
                                return staff ? staff.name : wo.lockRet.message;
                            });
                            if (WOLALT === 'alert') {
                                $F.alert(message);
                            } else {
                                $F.notify(message, 0, undefined, undefined, 3000);
                            }
                        }
                    } else {
                        $F.alert($F.lang('wo.lockedFailedAlert') + ': ' +  wo.lockRet.message);
                    }
                }
            }
            return instance;
        },

        /**
         * 加载工单数据
         * @param workorderGuid 重新加载的工单guid
         * @param taskId 重新加载的工单taskId
         * @param reopen 是否要页面重新打开，当步骤id不同时候，就需要重新打开了
         */
        loadWorkorderData : function (workorderGuid, taskId, reopen) {
            const wo = this, project = this.project;
            if (reopen) {//是否需要重新打开工单
                var tabName = wo.tabName;
                wo.clearInfo();
                project.openAddinWO3ByOId('AddinWO3', tabName, workorderGuid, taskId);
            } else {
                //清空instance里的xmldos请求，避免重复记录了之前的xmldos请求
                wo.instance.xmldosRemoveAll();
                let newFlag = wo.loadMeData({
                    workorderGuid,
                    taskId
                });
                if (newFlag === -1) {// 创建新工单加载数据失败
                    $F.log('Workorder3 - loadWorkorder', 'Error occurred on loading data');
                    return;
                }
                taskId = wo.getTaskGuid();
                wo.instance.objective.taskId = taskId;
                wo.instance.objective.taskBoundData = wo.taskBoundData;
                wo.newFlag = newFlag;
                var currentStepIdMap = wo.getCurStepIdMap();
                if (newFlag == 1) {
                    //new wo
                    wo.oldFrameId = "";
                    wo.oldStepIdMap = "";
                } else {
                    wo.oldFrameId = wo.getWorkOrderFrameId();
                    wo.oldStepIdMap = currentStepIdMap;
                }
                wo.frameId = wo.oldFrameId;
                wo.curStepIdMap = currentStepIdMap;
                wo.nextStepInfo.buildNextSteps();
                wo.setButtonStatus();

                var fields = wo.generatePropertiesFields();
                wo.instance.batchFireValue(fields, true);

                //要更新这个stepGuid
                wo.woStepGuid = Guid.raw();
            }
        },

        /**
         * 设置工单操作按钮状态
         *
         */
        setButtonStatus: function(needNotLock) {
            // 根据工单结构设置按钮状态
            this.buttonStatus.setCurrentWOStatus(needNotLock);
            // 根据substatus修改操作按钮状态
            this.buttonStatus.disableStatusExcept(this.getObjectiveSubStatus());
            // 根据加锁情况修改操作按钮状态
            this.buttonStatus.checkLock(this.lockRet);
        },

        /**
         * 加载工单数据，objective和elitetask相关数据
         */
        loadMeData : function (params) {
            const {workorderGuid, taskId, rolegroupId, objective} = params;
            const wo = this, project = this.project;
            wo.cacheAllExtField(this.WOEXPF);
            wo.woBoundData.setValue("objective_guid", workorderGuid);
            wo.guid = workorderGuid;

            const newWo = function(workorderGuid, rolegroupId) {
                const allStruct = wo.struct.getStruct();
                const firstStepId = wo.struct.getFirstNode({
                    rolegroupId
                });
                if (!firstStepId) {
                    $F.err('Workorder3 - loadMeData', 'No suitable first step found with rolegroupId: ' + rolegroupId);
                    return -1;
                }
                const step = wo.struct.getOneNode(firstStepId) || {};
                wo.newMeData({
                    workorderGuid,
                    customerGuid: wo.customer_guid,
                    allStruct,
                    step,
                    rolegroupId
                });
                wo.oldTaskBoundData = wo.taskBoundData.copy();
                wo.oldTaskBoundData.deleteFlag = 0;
                return 1;
            };
            const loadWo = function(objective, taskId) {
                wo.setWorkOrderFrameId(objective.getValue("map_id"));
                wo.setValue('map_id', objective.getValue("map_id"));
                wo.setCategory(objective.getValue("category"));
                wo.setPriorityId(objective.getValue("priority_id"));
                wo.setExpiredDate(objective.getValue("expireddate"));
                wo.setCustomerGuid(objective.getValue("customer_guid"));
                wo.setObjectiveStatus(objective.getValue("objectivestatus"));
                //wo.setExpectStartTime(data.value[0].fields[0][7]);
                wo.setForecastDate(objective.getValue("forecastdate"));
                wo.setCurStepIdMap(objective.getValue("curstep_id_map"));
                wo.setRolegroupId(objective.getValue("rolegroup_id"));
                wo.woBoundData.setValue('HANDLEGROUP_ID', objective.getValue("handlegroup_id"));
                wo.woBoundData.setValue('HANDLEBY_ID', objective.getValue("handleby_id"));

                //LoadExpField
                for (let i = 0, expfarr = wo.WOEXPF.split(","); i < expfarr.length; i++) {
                    let expf = expfarr[i].trim();
                    wo.setValue(expf, objective.getValue(expf));
                }

                if (taskId) {
                    //load taskBoundData: select * from elitetask{C1} where elitetask_guid = @{C2}S
                    let data = project.commonQuerySync("ECore.Workorder.selectEliteTask", [taskId], wo.dbPool);
                    if (!$F.isNull(data) && data.value[0].count > 0) {
                        const eliteTask = $F.makeJsonBean(data.value[0]);
                        $.each(eliteTask, function(key, value) {
                            if (key) {
                                key = key.toLowerCase();
                                if (Workorder.TASK_FIELDS.DATE_FIELDS.includes(key)) {
                                    if ($F.isDate(value)) {
                                        wo.taskBoundData.OldValue(key, value, $CONST.BoundType.F_Date);
                                    }
                                } else if (Workorder.TASK_FIELDS.NUMBER_FIELDS.includes(key)) {
                                    wo.taskBoundData.OldValue(key, value, $CONST.BoundType.F_Int);
                                } else {
                                    wo.taskBoundData.OldValue(key, value);
                                }
                            }
                        });
                        wo.setCurStepIdMap(eliteTask.CURSTEP_ID_MAP);
                        wo.setFrameMapId(eliteTask.MAP_ID);
                        wo.oldTaskBoundData = wo.taskBoundData.copy();
                        wo.oldTaskBoundData.deleteFlag = 0;
                    }
                }
                return 0;
            };

            if (objective) {
                // 可以直接用objective，不需要重新查询
                if (objective.flag === 0) { // 已有objective
                    return loadWo(objective, taskId);
                } else { // 新建的objective (objective.flag === 1)
                    return newWo(workorderGuid, rolegroupId)
                }
            } else { // 从loadWorkorderData里调用时候，需要重新查询数据
                let expFields = this.WOEXPF;
                if (expFields && !expFields.startWith(",")) {
                    expFields = "," + expFields
                }
                let data = project.commonQuerySync("ECore.Workorder.selectWODetailInfo3", [expFields, $E.staff.epidSql, workorderGuid], this.dbPool);
                if ($F.isEmpty(data)) {
                    return -1;
                }

                //select objective_guid,map_id,category,priority_id,expireddate,customer_guid,objectivestatus,expectstarttime,handlegroup_id,handleby_id,expiredhandleby_id,expiredhandlegroup_id,forecastdate,curstep_id_map{C1} from objective{C2} where objective_guid=@{C3}S
                if (data.value[0].count > 0) {
                    let objectiveBean = $F.makeJsonBean(data.value[0]);
                    const wrapBean = function(bean) {
                        bean.getValue = function(field) {
                            return bean[field.toUpperCase()];
                        };
                        return bean;
                    };
                    objectiveBean = wrapBean(objectiveBean);
                    return loadWo(objectiveBean, taskId);
                } else {
                    return newWo(workorderGuid, rolegroupId)
                }
            }
        },

        /**
         * 加载新增工单数据
         * @param params
         */
        newMeData: function (params) {
            const {workorderGuid, customerGuid, allStruct, step, rolegroupId} = params;
            const wo = this, project = this.project;
            wo.setObjectiveGuid(workorderGuid);
            wo.setCustomerGuid(customerGuid);
            wo.setWorkOrderFrameId($F.trimToEmpty(allStruct.MAP_ID));
            wo.setPriorityId(allStruct.PRIORITY_ID);
            wo.setCategory(allStruct.CATEGORY);
            wo.setWorkOrderStatus($CONST.WO.WO_SysOpen);
            wo.setHandleById($E.staff.id);
            wo.setHandleGroupId(project.groupId);

            wo.setExpiredHandleById(step.EXPIREDHANDLEBY_ID);
            wo.setExpiredHandleGroupId(step.EXPIREDHANDLEGROUP_ID);

            // 新建工单开始不计算，等到保存时刻才去计算
            // let currentTime = $F.getDbTime();
            // wo.calculateFrameDates(allStruct, rolegroupId, currentTime);
            // wo.calculateStepDates(step, currentTime);

            wo.setCurStepStatus($CONST.WO.WO_SysOpen);
            wo.setCurStepComments("");
            wo.setCurStepIdMap(step.NODEMAP_ID);

            wo.setTaskGuid(Guid.raw());
            wo.setFrameMapId(allStruct.MAP_ID);
            //存模板id
            var woFrameMap = project.getFrameMap(allStruct.MAP_ID);
            wo.setObjectiveTypeId(woFrameMap.workorderFrameId);
            //步骤优先级
            wo.setTaskPriority(step.PRIORITY_ID);
            wo.setRolegroupId(rolegroupId);
        },

        calculateStepDates: function (step, currentTime) {
            const wo = this;
            if (!wo.getExpectStartTime()) {
                let expectStartTime = wo.getNextEndDate(currentTime, 'H', step.NEXTBEGINTIME);
                wo.setExpectStartTime(expectStartTime);
            }
            if (!wo.getExpectEndTime()) {
                let stepExpectEndTime = wo.getNextEndDate(currentTime, step.EXPIREDPERIODTYPE_ID, step.EXPIREDPERIOD);
                wo.setExpectEndTime(stepExpectEndTime);
            }
            if (!wo.getStepForecastDate()) {
                let stepForecastDate = wo.getNextEndDate(currentTime, step.FORECASTUNIT, -step.FORECASTLEN);
                wo.setStepForecastDate(stepForecastDate);
            }
        },

        calculateFrameDates: function (allStruct, rolegroupId, currentTime) {
            const wo = this, project = this.project;
            let expiredPeriodTypeId = allStruct.EXPIREDPERIODTYPE_ID;
            let expiredPeriod = allStruct.EXPIREDPERIOD;
            let forecastUnit = allStruct.FORECASTUNIT;
            let forecastLen = allStruct.FORECASTLEN;
            let ret = project.commonQuerySync('ECore.Workorder.queryExpiredPeriod', [allStruct.MAP_ID, rolegroupId], this.dbPool);
            if (ret && ret.value[0].count > 0) {
                let expiredPeriodMaps = $F.makeJsonArray(ret.value[0]);
                if (expiredPeriodMaps.length > 0) {
                    let findEp;
                    expiredPeriodMaps.forEach(ep => {
                        if (ep.ROLEGROUP_ID === rolegroupId) {
                            findEp = ep;
                            return false;
                        } else {
                            findEp = ep;
                        }
                    });
                    expiredPeriodTypeId = findEp.EXPIREDPERIODTYPE_ID;
                    expiredPeriod = findEp.EXPIREDPERIOD;
                    forecastUnit = findEp.FORECASTUNIT;
                    forecastLen = findEp.FORECASTLEN;
                }
            }
            let mExpiredDate = wo.getExpiredDate();
            if (!mExpiredDate) {
                mExpiredDate = wo.getNextEndDate(currentTime, expiredPeriodTypeId, expiredPeriod);
                wo.setExpiredDate(mExpiredDate);
            }
            if (!wo.getForecastDate()) {
                let mForecastDate = wo.getNextEndDate(mExpiredDate, forecastUnit, -forecastLen);
                wo.setForecastDate(mForecastDate);
            }
        },


        /**
         * 生成taskBoundData对象
         */
        generateTaskBoundData : function (task, now) {
            const wo = this, commonProps = ['CURSTEP_ID_MAP', 'ELITETASK_GUID', 'HANDLEGROUP_ID', 'HANDLEBY_ID',
                    'HANDLEROLE_ID', 'COMMENTS', 'CURSTEPSTATUS', 'CURSTEP_ID_MAP', 'EXPECTSTARTTIME', 'EXPECTENDTIME', 'STEPFORECASTDATE'],
                stepIdMap = task.CURSTEP_ID_MAP, step = wo.struct.getOneNode(stepIdMap);
            if (step) {
                const tbd = new BoundData("elitetask" + $E.staff.epidSql);
                tbd.KeyInfoEdit("elitetask_guid");
                tbd.setCreateInfo("CREATEDDATE", "CREATEDBY", $E.staff.id);
                tbd.setModifyInfo("MODIFIEDDATE", "MODIFIEDBY", $E.staff.id);
                tbd.setValue('elitetask_guid', task.ELITETASK_GUID || Guid.raw());
                tbd.setValue('objective_guid', wo.guid);
                tbd.setValue('handlegroup_id', task.HANDLEGROUP_ID || '');
                tbd.setValue('handleby_id', task.HANDLEBY_ID || '');
                tbd.setValue('handlerole_id', task.HANDLEROLE_ID || '');
                tbd.setValue('comments', task.COMMENTS || '');
                tbd.setValue('customer_guid', wo.customer_guid);
                tbd.setValue('curstepstatus', task.CURSTEPSTATUS || 'OPEN');
                tbd.setValue('curstep_id', task.CURSTEP_ID_MAP || '');
                tbd.setValue('curstep_id_map', task.CURSTEP_ID_MAP || '');
                tbd.setValue('map_id', wo.struct.getStruct().MAP_ID);
                const expectStartTime = wo.getNextEndDate(now, 'H', step.NEXTBEGINTIME);
                tbd.setValue('expectstarttime', task.EXPECTSTARTTIME || expectStartTime, $CONST.BoundType.F_Date);
                const expectEndTime = wo.getNextEndDate(now, step.EXPIREDPERIODTYPE_ID, step.EXPIREDPERIOD);
                tbd.setValue('expectendtime', task.EXPECTENDTIME || expectEndTime, $CONST.BoundType.F_Date);
                const stepForecastDate = wo.getNextEndDate(now, step.FORECASTUNIT, -step.FORECASTLEN);
                tbd.setValue('stepforecastdate', task.STEPFORECASTDATE || stepForecastDate, $CONST.BoundType.F_Date);
                tbd.setValue('objectivesubstatus', step.DEFSUB || '');

                for (let key in task) {
                    if (!commonProps.contains(key)) {
                        let value = task[key];
                        $F.log('Workorder - generateTaskBoundData', "Set custom task prop:" + key + " - " + value);
                        key = key.toLowerCase();
                        if (Workorder.TASK_FIELDS.DATE_FIELDS.includes(key)) {
                            if ($F.isDate(value)) {
                                tbd.setValue(key, value, $CONST.BoundType.F_Date);
                            }
                        } else if (Workorder.TASK_FIELDS.NUMBER_FIELDS.includes(key)) {
                            tbd.setValue(key, value, $CONST.BoundType.F_Int);
                        } else {
                            tbd.setValue(key, value);
                        }
                    }
                }

                const mNewStep = wo.newStep({
                    workorderStepGuid: Guid.raw(),
                    workorderGuid : wo.guid,
                    oldHandleGroupId : wo.project.groupId,
                    oldHandleById : $E.staff.id,
                    handleGroupId : task.HANDLEGROUP_ID || '',
                    handleById : task.HANDLEBY_ID || '',
                    handleRoleId : task.HANDLEROLE_ID || '',
                    workorderStepStatus : $CONST.WO.WO_SysSendNextStep,
                    comments : task.COMMENTS || '',
                    stepExpired : task.EXPECTENDTIME || expectEndTime,
                    startTime : task.EXPECTSTARTTIME || expectStartTime,
                    fromStepIdMap : wo.getCurStepIdMap(),
                    toStepIdMap : task.CURSTEP_ID_MAP || '',
                    sendStepInfo : wo.getSendStepInfo()
                });
                tbd.SubTableAdd('step', mNewStep);
                return tbd;
            }
        },

        /**
         * 根据给定时间来计算期望结束时间
         * @param baseTime 基准时间
         * @param stepId （可选）步骤id，如果不传递默认为当前步骤
         */
        calculateExpectEndTime : function(baseTime, stepId) {
            stepId = stepId || this.getCurStepIdMap();
            const step = this.struct.getOneNode(stepId);
            if (step) {
                var expectEndTime = this.getNextEndDate(baseTime, step.EXPIREDPERIODTYPE_ID, step.EXPIREDPERIOD);
                this.setExpectEndTime(expectEndTime);
                return expectEndTime;
            }
        },

        /**
         * 用来做保存下送之类操作之前，通过调用命令组方式来决定是不是可以继续操作下去，针对多组件情况下，只需要有一个组件说不能执行，就不能执行了
         * @param cmdId
         * @param ev
         * @returns {boolean}
         */
        checkBeforeOperate : function(cmdId, ev) {
            var wo = this, validation = true;
            if (!ev) {
                ev = {};
            }
            var allComponents = CtlUtils.findAllComponents(this.instance.runtime);
            $.each(allComponents, function(key, com) {
                com.doCmd(cmdId, ev, true);
                if (WOUtils.getFlag(ev, "ExecFlag") <= 0) {
                    validation = false;
                    $F.log('Workorder3 - checkBeforeOperate', 'Check failed in component ' + com.id);
                    return false;
                }
            });
            if (validation) {
                wo.instance.doCmd(cmdId, ev, wo.instance.runtime, wo.instance.id, true);
                if (WOUtils.getFlag(ev, "ExecFlag") <= 0) {
                    $F.log('Workorder3 - checkBeforeOperate', 'Check failed');
                    validation = false;
                }
            }
            return validation;
        },


        /**
         * 工单保存操作
         * @param closeFlag
         * @returns {Number}
         */
        save : function (closeFlag) {
            const wo = this, project = this.project;
            if (wo.buttonStatus.getSaveStatus() != 0) {
                $F.log('Workorder3 - save', 'No save priority');
                $F.alert($F.lang('wo.noSavePriorityAlert'));
                return -1;
            }
            if (wo.buttonStatus.getNewFlag() != 1) {
                if (wo.validCanOperate() <= 0) {
                    $F.log('Workorder3 - save', 'No save priority, operator has been changed.');
                    return -1;
                }
            }

            //调用 "保存前前" 命令组 122，检查是否可以保存
            if (wo.instance.struct.version === 'DYN2') {
                if (!wo.checkBeforeOperate('wo.save.prepare')) {
                    return -1;
                }
            } else {
                if (!wo.checkBeforeOperate('P122')) {
                    return -1;
                }
            }

            //调用保存前命令组
            if (wo.instance.struct.version === 'DYN2') {
                if (!wo.checkBeforeOperate('wo.save.before')) {
                    return -1;
                }
            } else {
                if (!wo.checkBeforeOperate('P112')) {
                    return -1;
                }
            }

            var mNewStep, mSavemain;
            mNewStep = wo.newStep({
                workorderGuid : wo.guid,
                oldHandleGroupId : project.groupId,
                oldHandleById : $E.staff.id,
                workorderStepStatus : $CONST.WO.WO_SysClose,
                comments : wo.getCurStepComments(),
                startTime : wo.getExpectStartTime(),
                fromStepIdMap : wo.getCurStepIdMap(),
                toStepIdMap : '',
                sendStepInfo : wo.getSendStepInfo()
            });

//            对于创建工单（save）
//            Step表 根据 当前Node的配置，有R就记录，没有就为空，G和S均记录；
//            Task表 根据Node的配置，有R就记录，没有就为空，G和S均记录；
            if (wo.newFlag == 1) {
                var currentStep = wo.struct.getOneNode(wo.getCurStepIdMap());
                var handleType = wo.fetchHandleType(currentStep);
                var sendType = wo.fetchSendType(currentStep);
                if (handleType === 'R' || handleType === 'RG') {
                    if (sendType === 4) { //原步骤处理角色，在新建工单时候，第一步是不允许原步骤的
                        $F.log("Workorder3 - save", "保存失败，第一步不允许是原步骤处理角色");
                        $F.alert($F.lang('wo.saveFailedAlert'));
                        return -1;
                    }
                    if (currentStep.HANDLEGROUP_ID) {//这里的HANDLEGROUP_ID存的是某个角色， 如果有值，就存这个
                        mNewStep.setValue('oldhandlerole_id', currentStep.HANDLEGROUP_ID);
                        wo.taskBoundData.setValue('handlerole_id', currentStep.HANDLEGROUP_ID);
                    }
                }
                let currentTime = $F.getDbTime();
                wo.calculateFrameDates(wo.struct.getStruct(), wo.getRolegroupId(), currentTime);
                wo.calculateStepDates(currentStep, currentTime);
            }
            wo.setCurStepStatus($CONST.WO.WO_SysOpen);
            wo.setWorkOrderStatus($CONST.WO.WO_SysOpen);
            this.setOBFlagByNodeMapId(wo.getCurStepIdMap());

            mSavemain = wo.taskBoundData;
            mSavemain.SubTableAdd("step", mNewStep);
            if (wo.oldFrameId && wo.oldFrameId !== wo.frameId) {
                var mNewPath = wo.newPath({
                    workorderGuid: wo.guid,
                    handleGroupId: project.groupId,
                    handleById: $E.staff.id,
                    frameId: wo.frameId,
                    fromFrameId: wo.oldFrameId,
                    fromStepIdMap: wo.oldStepIdMap,
                    toStepIdMap: wo.getCurStepIdMap()
                });
                mSavemain.SubTableAdd("Change", mNewPath);
            }
            wo.setObjectiveStatus('OPEN');
            // 如果是代理人模式，则不修改处理人
            if (wo.originalHandleBy) {
                wo.setHandleById(wo.getOriginalHandleBy());
                wo.setHandleGroupId(wo.originalHandleGroup);
            } else {
                wo.setHandleById($E.staff.id);
            }
            wo.operate = 'save';
            wo.instance.xmldosRequest(wo.dbPool, mSavemain.getData());
            let saveRet = wo.instance.save();
            wo.instance.xmldosRemoveAll();
            if (saveRet > 0) {
                if (wo.instance.struct.version === 'DYN2') {
                    wo.instance.doCmdIncludeComponents('wo.save.after', {}, wo.instance.runtime, wo.instance.id, true);
                } else {
                    wo.instance.doCmdIncludeComponents("P102");
                }

                if (closeFlag !== 0) {
                    wo.clearInfo();
                } else {
                    //如果保存后不关闭工单，则需要更新这个stepGuid
                    wo.woStepGuid = Guid.raw();
                    var oldLockKey = wo.genLockKey();
                    var newLockKey = wo.genLockKey(true);
                    // 如果lockKey改变了
                    if (oldLockKey !== newLockKey) {
                        // 解锁原来的lockKey
                        project.doUnlock(oldLockKey, '', wo.dbPool);
                        // 加锁新的lockKey
                        wo.doLock(newLockKey);
                    }
                }
                return 1;
            } else {
                $F.log("Workorder3 - save", "Save failed, please check the server log.");
                $F.alert($F.lang('wo.saveFailedAlert'));
                return -1;
            }
        },

        /**
         * 工单下送
         * @param vSendFlag
         * @returns {Number}
         */
        send : function (vSendFlag) {
            var wo = this, project = this.project;
            if (wo.buttonStatus._SendStatus != 0) {
                $F.log('Workorder3 - send', 'No send priority');
                $F.alert($F.lang('wo.noSendPriorityAlert'));
                return -1;
            }
            if (wo.buttonStatus.getNewFlag() != 1) {
                if (wo.validCanOperate() <= 0) {
                    $F.log('Workorder3 - send', 'No send priority, operatior has been changed.');
                    return -1;
                }
            }
            //校验本步骤协办工单完成情况
            var currentStep = wo.struct.getOneNode(wo.getCurStepIdMap());
            if ($F.isNull(currentStep)) {
                $F.log('Workorder3 - send', 'Current step [' + wo.getCurStepIdMap() + '] not exist.');
                $F.alert($F.lang('wo.currentStepNotExists'));
                return -1;
            }

            //调用 "下送前前" 命令组 123，校验是否可以下送
            if (wo.instance.struct.version === 'DYN2') {
                if (!wo.checkBeforeOperate('wo.send.prepare')) {
                    return -1;
                }
            } else {
                if (!wo.checkBeforeOperate('P123')) {
                    return -1;
                }
            }


            //正常模式
            let sendToStep, handleGroup, handleBy, mDateBegin, mDateEnd, handleRole = '', sendToLink, handleType, sendType;
            wo.nextStepInfo.getNextStepping();
            sendToStep = wo.getInstancePropertyValue(WOUtils.PROPERTY.NEXT_STEP_ID);
            if (!$F.isEmpty(sendToStep)) {
                if (!wo.nextStepInfo.getCanNextStep(sendToStep)) {
                    $F.alert($F.lang('wo.cannotSentToStepAlert'));
                    $F.log('Workorder3 - send', 'Cannot send to step: ' + sendToStep);
                    return -1;
                }
            } else {
                if (vSendFlag == 0) {
                    let nextStepLink = wo.nextStepInfo.getOneNextStep(true);
                    sendToStep = nextStepLink.id;
                    sendToLink = nextStepLink.link;
                }
            }

            $F.log('Workorder3 - send', 'WorkOrder Set Next Step:' + sendToStep);
            const nextStep = wo.struct.getOneNode(sendToStep);
            if ($F.isNull(nextStep)) {
                $F.alert($F.lang('wo.sendToStepNotExists') + ' : ' + sendToStep);
                $F.log('Workorder3 - send', 'Next step ' + sendToStep + ' not exists.');
                return -1;
            }
            handleGroup = wo.getInstancePropertyValue(WOUtils.PROPERTY.NEXT_STEP_GROUP_ID);
            handleBy = wo.getInstancePropertyValue(WOUtils.PROPERTY.NEXT_STEP_STAFF_ID);
            handleRole = wo.getInstancePropertyValue(WOUtils.PROPERTY.NEXT_STEP_ROLE_ID);
            const sendHandler = wo.fetchSendHandler({ currentStep, nextStep, handleGroup, handleBy, handleRole });
            if (!sendHandler) {
                return -1;
            }
            handleBy = sendHandler.handleBy;
            handleGroup = sendHandler.handleGroup;
            handleRole = sendHandler.handleRole;
            handleType = sendHandler.handleType;
            sendType = sendHandler.sendType;

            $F.log('Workorder3 - send', 'WorkOrder sendTo:' + sendToStep + ' sendBy:' + handleBy + ' sendGrp:' + handleGroup + ' sendRole:' + handleRole + ' handleType: ' + handleType + ' sendType: ' + sendType);

            const mExecObj = {
                NEXTSTEP: sendToStep,
                NEXTSTEPMAP: sendToStep,
                NEXTGRP: handleGroup,
                NEXTBY: handleBy,
                NEXTROLE: handleRole
            };

            //调用下送前命令组 (还是需要有验证的逻辑，为了和老的兼容。其实验证应该写到前前命令组里去)
            if (wo.instance.struct.version === 'DYN2') {
                if (!wo.checkBeforeOperate('wo.send.before', mExecObj)) {
                    return -1;
                }
            } else {
                if (!wo.checkBeforeOperate('P113', mExecObj)) {
                    return -1;
                }
            }
            let now = $F.getDbTime();
            mDateBegin = wo.getInstancePropertyValue(WOUtils.PROPERTY.NEXT_STEP_BEGIN_DATE);
            mDateEnd = wo.getInstancePropertyValue(WOUtils.PROPERTY.NEXT_STEP_EXPIRE_DATE);
            if ($F.isEmpty(mDateBegin)) {
                mDateBegin = now;
                if (nextStep.NEXTBEGINTIME > 0) {
                    mDateBegin = $F.dateAddFormat(mDateBegin, "hour", nextStep.NEXTBEGINTIME);
                }
            }
            if ($F.isEmpty(mDateEnd)) {
                mDateEnd = wo.getNextEndDate(mDateBegin, nextStep.EXPIREDPERIODTYPE_ID, nextStep.EXPIREDPERIOD);
            }
            const mForecastDate = wo.getNextEndDate(mDateEnd, nextStep.FORECASTUNIT, -(nextStep.FORECASTLEN));
            const mStepComments = wo.getCurStepComments();
            const mCurNodeIDMap = wo.getCurStepIdMap();
            const mNewStep = wo.newStep({
                workorderGuid : wo.guid,
                oldHandleGroupId : project.groupId,
                oldHandleById : $E.staff.id,
                handleGroupId : handleGroup,
                handleById : handleBy,
                workorderStepStatus : $CONST.WO.WO_SysSendNextStep,
                comments : mStepComments,
                stepExpired : mDateEnd,
                startTime : wo.getExpectStartTime(),
                fromStepIdMap : mCurNodeIDMap,
                toStepIdMap : sendToStep,
                sendStepInfo : wo.getSendStepInfo(),
                handleRoleId : handleRole
            });
            const mNewSendPath = wo.newPath({
                workorderGuid: wo.guid,
                handleGroupId: project.groupId,
                handleById: $E.staff.id,
                frameId: wo.frameId,
                fromFrameId: "",
                fromStepIdMap: mCurNodeIDMap,
                toStepIdMap: sendToStep
            });
            wo.setOBFlagByNodeMapId(sendToStep);

            if (wo.newFlag == 1) {
                // 对于创建工单（send）
                // Step表 oldhandle根据 当前Node的配置，有R就记录，没有就为空，G和S均记录；
                if (handleType === 'R' || handleType === 'RG') {
                    if (currentStep.HANDLEGROUP_ID) {//这里的HANDLEGROUP_ID存的是某个角色， 如果有值，就存这个
                        mNewStep.setValue('oldhandlerole_id', currentStep.HANDLEGROUP_ID);
                        mNewSendPath.setValue('handlerole_id', currentStep.HANDLEGROUP_ID);
                    }
                }
                wo.calculateFrameDates(wo.struct.getStruct(), wo.getRolegroupId(), now);
                wo.calculateStepDates(currentStep, now);
            } else {
                // 对于非创建工单（send）
                // Step表 oldhandle根据 当前Task的配置，有R就记录，没有就为空，G和S均记录；
                if (wo.taskBoundData.getValue('handlerole_id')) {
                    mNewStep.setValue('oldhandlerole_id', wo.taskBoundData.getValue('handlerole_id'));
                    mNewSendPath.setValue('handlerole_id', wo.taskBoundData.getValue('handlerole_id'));
                }
            }

            //删除原有task update或者insert新的task
            const mSavemain = wo.taskBoundData;
            if (wo.operateTaskGuid) {
                var operateTaskBoundData;
                if (wo.checkTaskExists(wo.operateTaskGuid)) {
                    //如果存在的task，则就更新comments
                    operateTaskBoundData = new BoundData("elitetask" + $E.staff.epidSql);
                    operateTaskBoundData.KeyInfoEdit("elitetask_guid");
                    operateTaskBoundData.setCreateInfo("CREATEDDATE", "CREATEDBY", $E.staff.id);
                    operateTaskBoundData.setModifyInfo("MODIFIEDDATE", "MODIFIEDBY", $E.staff.id);
                    operateTaskBoundData.setValue('comments', wo.getCurStepComments());
                } else {
                    //如果不存在，则需要insert所有字段
                    operateTaskBoundData = wo.setNewTaskValue(sendToStep, mDateBegin, handleGroup, handleBy, handleRole, nextStep.EXPIREDHANDLEGROUP_ID, nextStep.EXPIREDHANDLEBY_ID, mDateEnd, mForecastDate, nextStep.PRIORITY_ID);
                    wo.sendMailOnNewTask(operateTaskBoundData.getValue('handleby_id'));
                    wo.ccOnNewTask(operateTaskBoundData, wo.operateTaskGuid, nextStep, sendToLink);
                }
                operateTaskBoundData.setValue('elitetask_guid', wo.operateTaskGuid);
                mSavemain.SubTableAdd('optask', operateTaskBoundData);
                wo.operateTaskBoundData = operateTaskBoundData;
            } else {
                //不指定，就不做操作
            }
            //删除原来task, 并且添加一个elitetask_his
            wo.deleteTaskAndAddHis(mSavemain);

            mSavemain.SubTableAdd("step", mNewStep);
            mSavemain.SubTableAdd("path", mNewSendPath);
            wo.deleteAppCall(mSavemain);
            wo.setObjectiveStatus('OPEN');

            if (!$F.isEmpty(wo.oldFrameId) && wo.oldFrameId !== wo.frameId) {
                var mNewPath = wo.newPath({
                    workorderGuid: wo.guid,
                    handleGroupId: project.groupId,
                    handleById: $E.staff.id,
                    frameId: wo.frameId,
                    fromFrameId: wo.oldFrameId,
                    fromStepIdMap: mCurNodeIDMap,
                    toStepIdMap: sendToStep
                });
                mSavemain.SubTableAdd("Change", mNewPath);
            }

            // 考虑是否执行过addTasks并且非立即存数据
            if (wo.subTasks && wo.subTasks.length > 0) {
                wo.subTasks.forEach((task, i) => {
                    var tbd = wo.generateTaskBoundData(task, now);
                    mSavemain.addSub("subtask" + i, tbd);
                    wo.sendMailOnNewTask(tbd.getValue('handleby_id'));
                    wo.ccOnNewTask(tbd, tbd.getValue('elitetask_guid'), wo.struct.getOneNode(tbd.getValue('curstep_id_map')));
                });
            }

            // 考虑自动创建子任务的task
            this.struct.getAllNodes().forEach((node, i) => {
                if (node.nodeMapExtends && node.nodeMapExtends["AUTOADD"] && node.nodeMapExtends["MAINTASK"]) {
                    let autoAdd = node.nodeMapExtends["AUTOADD"].EXTENDVALUE;
                    let mainTask = node.nodeMapExtends["MAINTASK"].EXTENDVALUE;
                    if (autoAdd == 1 && mainTask === sendToStep) {
                        const sendHandler = wo.fetchSendHandler({ currentStep, nextStep: node, handleGroup,  handleBy,  handleRole});
                        if (!sendHandler) {
                            return;
                        }
                        // 判断是否处理人已经有此步骤的task，有就不重复创建
                        let ret = project.commonQuerySync('ECore.Workorder.checkTaskExist', [sendHandler.handleBy, node.NODEMAP_ID, this.guid], this.dbPool);
                        if (ret && ret.value[0].count > 0) {
                            $F.log('Workorder3 - send', 'Find duplicate task for [' + sendHandler.handleBy + '] on step ' + node.NODEMAP_ID);
                            return;
                        }
                        let tbd = this.generateTaskBoundData({
                            HANDLEGROUP_ID: sendHandler.handleGroup,
                            HANDLEBY_ID: sendHandler.handleBy,
                            CURSTEP_ID_MAP: node.NODEMAP_ID,
                            MAINTASKSTEP_ID_MAP: sendToStep
                        }, now);
                        mSavemain.addSub('sub_task_' + i, tbd);
                        $F.log('Workorder3 - send', 'Auto create sub task: ' + tbd.getValue('elitetask_guid'));
                    }
                }
            });
            wo.operate = 'send';
            wo.instance.xmldosRequest(wo.dbPool, mSavemain.getData());
            let saveRet = wo.instance.save();
            wo.instance.xmldosRemoveAll();
            if (saveRet > 0) {
                if (wo.instance.struct.version === 'DYN2') {
                    wo.instance.doCmdIncludeComponents('wo.send.after', mExecObj, wo.instance.runtime, wo.instance.id, true);
                } else {
                    wo.instance.doCmdIncludeComponents("P103", mExecObj);
                }
                wo.clearInfo();
                return 1;
            } else {
                $F.log('Workorder3 - WOSendNextStep', 'Send failed, please check the server log.');
                $F.alert($F.lang('wo.sendFailedAlert'));
                return -1;
            }
        },

        /**
         * 工单关闭或者重新打开，考虑的closeOrReopen这个状态
         * @returns {*}
         */
        close : function () {
            var wo = this, project = this.project;
            if (wo.buttonStatus.getCloseOrOpenStatus() != 0) {
                $F.alert($F.lang('wo.noClosePriorityAlert'));
                $F.log('Workorder3 - close', 'No right for close.');
                return -1;
            }
            $F.log('Workorder3 - close', 'In Wo_CloseMe Function');
            if (wo.buttonStatus.getCloseFlag() == 0) {//非关闭工单
                $F.log('Workorder3 - close', 'In Wo_CloseMe Function Close WO');
                return this.closeWorkorder();
            } else {//已关闭工单
                $F.log('Workorder3 - close', 'In Wo_CloseMe Function OPEN WO');
                return this.reopenWorkOrder();
            }
        },

        /**
         * 工单关闭或者重新打开，根据工单是不是已经关闭，分别考虑end状态和reopen状态
         * @returns {*}
         */
        closeAndReopen : function () {
            var wo = this, project = this.project;
            $F.log('Workorder3 - close', 'In Wo_CloseMe Function');
            if (wo.buttonStatus.getCloseFlag() == 0) {//非关闭工单
                if (wo.buttonStatus.getEndStatus() != 0) {
                    $F.alert($F.lang('wo.noClosePriorityAlert'));
                    $F.log('Workorder3 - close', 'No right for close.');
                    return -1;
                }
                $F.log('Workorder3 - close', 'Close workorder.');
                return this.closeWorkorder();
            } else {//已关闭工单
                if (wo.buttonStatus.getReOpenStatus() != 0) {
                    $F.alert($F.lang('wo.noReopenPriorityAlert'));
                    $F.log('Workorder3 - close', 'No right for reopen.');
                    return -1;
                }
                $F.log('Workorder3 - close', 'Reopen workorder.');
                return this.reopenWorkOrder();
            }
        },

        /**
         * 工单关闭
         * @returns {Number}
         */
        closeWorkorder : function () {
            var wo = this, project = this.project;
            if (wo.buttonStatus.getNewFlag() != 1) {
                if (wo.validCanOperate() < 0) {
                    $F.log('Workorder3 - closeWorkorder', 'No right for close, current handler has changed');
                    return -1;
                }
            }
            var mCurrentStep = wo.struct.getOneNode(wo.getCurStepIdMap());
            if (mCurrentStep == null) {
                $F.alert($F.lang('wo.currentStepNotExists'));
                $F.log('Workorder3 - closeWorkorder', 'No current step found, please check the config.');
                return -1;
            }

            //todo: 是否要判断其他task是不是都结束     MULTITASK TASKFLAG
            //var iCount = mCurrentStep.Sub.length;
            //var mMustProce = 0;
            //for (var i=0;i<iCount;i++){
            //    var mTmp = mCurrentStep.Sub[i];
            //    if (mTmp.MustProcess == 1){
            //        mMustProce = 1;
            //        break;
            //    }
            //}
            //if (mMustProce == 1){
            //    if (checkSubStepFinished(mCurrentStep) < 0){
            //        $F.alert("协办工单任务没有完成，不能结案");
            //        return -1;
            //    }
            //}

            //调用结案前前命令组，校验是否可以结案
            if (wo.instance.struct.version === 'DYN2') {
                if (!wo.checkBeforeOperate('wo.close.prepare')) {
                    return -1;
                }
            } else {
                if (!wo.checkBeforeOperate('P124')) {
                    return -1;
                }
            }

            //调用结案前命令组
            if (wo.instance.struct.version === 'DYN2') {
                if (!wo.checkBeforeOperate('wo.close.before')) {
                    return -1;
                }
            } else {
                if (!wo.checkBeforeOperate('P114')) {
                    return -1;
                }
            }

            var mNewStep = wo.newStep({
                workorderGuid : wo.guid,
                oldHandleGroupId : project.groupId,
                oldHandleById : $E.staff.id,
                workorderStepStatus : $CONST.WO.WO_SysEnd,
                comments : wo.getCurStepComments(),
                startTime : wo.getExpectStartTime(),
                fromStepIdMap : wo.getCurStepIdMap(),
                sendStepInfo : wo.getSendStepInfo()
            });
            wo.setCurStepStatus($CONST.WO.WO_SysEnd);
            //不在这里close，由变量来决定是否要close
            //wo.setWorkOrderStatus($CONST.WO.WO_SysClose);
            //wo.setObjectiveStatus('CLOSE');
            if (!wo.originalHandleBy) { // 代理人情况不修改处理人和组
                wo.setHandleById($E.staff.id);
                wo.setHandleGroupId(project.groupId);
            }
            const mSavemain = wo.taskBoundData;
            wo.deleteTaskAndAddHis(mSavemain);
            mSavemain.SubTableAdd("step", mNewStep);
            wo.deleteAppCall(mSavemain);

            if (!$F.isEmpty(wo.oldFrameId) && wo.oldFrameId !== wo.frameId) {
                var mNewPath = wo.newPath({
                    workorderGuid: wo.guid,
                    handleGroupId: project.groupId,
                    handleById: $E.staff.id,
                    frameId: wo.frameId,
                    fromFrameId: wo.oldFrameId,
                    fromStepIdMap: wo.oldStepIdMap,
                    toStepIdMap: wo.getCurStepIdMap()
                });
                mSavemain.SubTableAdd("Change", mNewPath);
            }
            wo.operate = 'close';
            wo.instance.xmldosRequest(wo.dbPool, mSavemain.getData());
            let saveRet = wo.instance.save();
            wo.instance.xmldosRemoveAll();
            if (saveRet > 0) {
                var struct = wo.struct.getStruct();
                if (struct.CANGENERATEOB == 1) { //插入回访记录
                    if (wo.customer_guid != "1") {
                        var iRateNum = Math.floor(Math.random() * 100) + 1;//MyFunction.getRndNum(100);
                        if (iRateNum <= struct.GENERATERATE) {
                            wo.insertReplyCustomer(struct.REAL_GROUP_IDA2, wo.customer_guid, wo.guid);
                        }
                    }
                }
                if (wo.instance.struct.version === 'DYN2') {
                    wo.instance.doCmdIncludeComponents('wo.close.after', {}, wo.instance.runtime, wo.instance.id, true);
                } else {
                    wo.instance.doCmdIncludeComponents("P104");
                }
                wo.clearInfo();
                return 1;
            } else {
                $F.log('Workorder3 - closeWorkorder', 'Close failed, please check the server log.');
                $F.alert($F.lang('wo.closeFailedAlert'));
                return -1;
            }
        },

        /**
         * 重新打开工单
         * @returns {Number}
         */
        reopenWorkOrder : function () {
            var wo = this, project = this.project;
            //调用重新打开前前命令组，校验是否可以重新打开
            if (wo.instance.struct.version === 'DYN2') {
                if (!wo.checkBeforeOperate('wo.reopen.prepare')) {
                    return -1;
                }
            } else {
                if (!wo.checkBeforeOperate('P124')) {
                    return -1;
                }
            }

            //调用重新打开前命令组
            if (wo.instance.struct.version === 'DYN2') {
                if (!wo.checkBeforeOperate('wo.reopen.before')) {
                    return -1;
                }
            } else {
                if (!wo.checkBeforeOperate('P114')) {
                    return -1;
                }
            }

            var mNewStep = wo.newStep({
                workorderGuid : wo.guid,
                oldHandleGroupId : project.groupId,
                oldHandleById : $E.staff.id,
                workorderStepStatus : $CONST.WO.WO_SysReOpen,
                comments : wo.getCurStepComments(),
                startTime : wo.getExpectStartTime(),
                fromStepIdMap : wo.getCurStepIdMap(),
                sendStepInfo : wo.getSendStepInfo()
            });
            wo.setCurStepStatus($CONST.WO.WO_SysOpen);
            wo.setWorkOrderStatus($CONST.WO.WO_SysOpen);
            if (!wo.originalHandleBy) {
                wo.setHandleById($E.staff.id);
                wo.setHandleGroupId(project.groupId);
            }
            wo.setObjectiveStatus('OPEN');

            var mapId = wo.woBoundData.getValue('map_id');
            var woFrameMap = project.getFrameMap(mapId);

            //重新打开时候需要插入task，所以要对task相关参数赋值
            var taskId = Guid.raw();
            wo.taskBoundData.setValue("elitetask_guid", taskId);
            wo.taskBoundData.setValue('objective_guid', wo.woBoundData.getValue('objective_guid'));
            wo.taskBoundData.setValue('objectivetype_id', woFrameMap.workorderFrameId);
            wo.setFrameMapId(mapId);
            wo.setOBFlagByNodeMapId(wo.getCurStepIdMap());//看看是不是ob的工单
            var currentStepId = wo.getCurStepId(), reopenStepIdMap = wo.getCurrentMapExtends('REOPEN'), reopen = false;
            //判断重新打开时候，是不是节点有变化，如果有变化则需要整体页面重新打开
            if (reopenStepIdMap && !$F.equalsIgnoreCase(currentStepId, reopenStepIdMap)) {
                currentStepId = reopenStepIdMap;
                wo.setCurStepIdMap(currentStepId);
                reopen = true;
            }
            var step = wo.struct.getOneNode(currentStepId);
            if (step) {
                var mCurDate = $F.getDbTime();
                var expectStartTime = wo.getNextEndDate(mCurDate, 'H', step.NEXTBEGINTIME);
                wo.setExpectStartTime(expectStartTime);
                var stepExpectEndTime = wo.getNextEndDate(mCurDate, step.EXPIREDPERIODTYPE_ID, step.EXPIREDPERIOD);
                wo.setExpectEndTime(stepExpectEndTime);
                var stepForecastDate = wo.getNextEndDate(mCurDate, step.FORECASTUNIT, -step.FORECASTLEN);
                wo.setStepForecastDate(stepForecastDate);
                wo.taskBoundData.setValue('elitetaskstatus', 'OPEN');
                wo.taskBoundData.setValue('objectivesubstatus', step.DEFSUB || '');
                wo.sendMailOnNewTask(wo.taskBoundData.getValue('handleby_id'));
                wo.ccOnNewTask(wo.taskBoundData, taskId, step);
                var mSavemain = wo.taskBoundData;
                mSavemain.SubTableAdd("step", mNewStep);
                wo.operate = 'reopen';
                wo.instance.xmldosRequest(wo.dbPool, mSavemain.getData());
                let saveRet = wo.instance.save();
                wo.instance.xmldosRemoveAll();
                if (saveRet > 0) {
                    if (wo.instance.struct.version === 'DYN2') {
                        wo.instance.doCmdIncludeComponents('wo.reopen.after', {}, wo.instance.runtime, wo.instance.id, true);
                    } else {
                        wo.instance.doCmdIncludeComponents("P104");
                    }
                    wo.loadWorkorderData(wo.guid, taskId, reopen);
                    return 1;
                } else {
                    $F.log('Workorder3 - reopenWorkOrder', 'Reopen workorder failed.');
                    $F.alert($F.lang('wo.reopenFailedAlert'));
                    return -1;
                }
            } else {
                $F.log('Workorder3 - reopenWorkOrder', 'Reopen workorder failed, no step found: ' + currentStepId);
                $F.alert($F.lang('wo.reopenFailedAlert'));
                return -1;
            }
        },

        /**
         * 工单取消操作
         */
        cancel : function () {
            var wo = this;
            if (wo.instance.struct.version === 'DYN2') {
                if (!wo.checkBeforeOperate('wo.cancel')) {
                    return -1;
                }
            } else {
                if (!wo.checkBeforeOperate('P106')) {
                    return -1;
                }
            }
            wo.clearInfo();
            return 1;
        },


        /**
         * 失败结束工单
         * @returns {number}
         */
        failedClose : function () {
            var wo = this, project = this.project;
            if (wo.buttonStatus.getFailedStatus() != 0) {
                return -1;
            }
            if (wo.buttonStatus.getNewFlag() != 1) {
                if (wo.validCanOperate() < 0) {
                    return -1;
                }
            }
            var mCurrentStep = wo.struct.getOneNode(wo.getCurStepIdMap());
            if (!mCurrentStep) {
                $F.alert($F.lang('wo.currentStepNotExists'));
                $F.log('Workorder3 - failedClose', 'No current step found, please check the config.');
                return -1;
            }

            //todo: 以前的协办判断，现在要怎么处理？
            //var iCount = mCurrentStep.Sub.length;
            //var mMustProce = 0;
            //for (var i=0;i<iCount;i++){
            //    var mTmp = mCurrentStep.Sub[i];
            //    if (mTmp.MustProcess == 1){
            //        mMustProce = 1;
            //        break;
            //    }
            //}
            //if (mMustProce == 1){
            //    if (checkSubStepFinished(mCurrentStep) < 0){
            //        $F.alert("协办工单任务没有完成，不能失败结案");
            //        return -1;
            //    }
            //}
            //调用失败结案前前命令组，校验是否可以失败结案
            if (wo.instance.struct.version === 'DYN2') {
                if (!wo.checkBeforeOperate('wo.fail.prepare')) {
                    return -1;
                }
            } else {
                if (!wo.checkBeforeOperate('P128')) {
                    return -1;
                }
            }

            //调用失败结案前命令组
            if (wo.instance.struct.version === 'DYN2') {
                if (!wo.checkBeforeOperate('wo.fail.before')) {
                    return -1;
                }
            } else {
                if (!wo.checkBeforeOperate('P118')) {
                    return -1;
                }
            }

            var mNewStep = wo.newStep({
                workorderGuid : wo.guid,
                oldHandleGroupId : project.groupId,
                oldHandleById : $E.staff.id,
                workorderStepStatus : $CONST.WO.WO_SysFailed,
                comments : wo.getCurStepComments(),
                startTime : wo.getExpectStartTime(),
                fromStepIdMap : wo.getCurStepIdMap(),
                sendStepInfo : wo.getSendStepInfo()
            });
            if (!$F.isEmpty(wo.oldFrameId)) {
                wo.frameId = wo.oldFrameId;
            }

            //不在这里close，由变量来决定是否要close
            //wo.setWorkOrderStatus($CONST.WO.WO_SysFailed);
            //wo.setObjectiveStatus('CLOSE');

            var mSavemain = wo.taskBoundData;
            wo.deleteTaskAndAddHis(mSavemain);
            mSavemain.SubTableAdd("step", mNewStep);
            wo.operate = 'failedClose';
            wo.instance.xmldosRequest(wo.dbPool, mSavemain.getData());
            let saveRet = wo.instance.save();
            wo.instance.xmldosRemoveAll();
            if (saveRet > 0) {
                if (wo.instance.struct.version === 'DYN2') {
                    wo.instance.doCmdIncludeComponents('wo.fail.after', {}, wo.instance.runtime, wo.instance.id, true);
                } else {
                    wo.instance.doCmdIncludeComponents("P108");
                }
                wo.clearInfo();
                return 1;
            } else {
                wo.setCurStepStatus($CONST.WO.WO_SysOpen);
                wo.setWorkOrderStatus($CONST.WO.WO_SysOpen);
                $F.log('Workorder3 - failedClose', 'FailedClose failed, please check the server log.');
                $F.alert($F.lang('wo.failedCloseFailedAlert'));
                return -1;
            }
        },

        /**
         * 工单接管
         * @returns {number}
         */
        takeOver : function () {
            var wo = this, project = this.project;
            if (wo.buttonStatus.getTakeOverStatus() != 0) {
                $F.alert($F.lang('wo.noTakeoverPriorityAlert'));
                $F.log('Workorder3 - takeOver', 'No right for takeover');
                return -1;
            }
            // 加锁
            var lockKey = wo.genLockKey();
            var lockRet = wo.doLock(lockKey);
            if (!lockRet) {
                $F.alert($F.lang('wo.noTakeoverPriorityLockedFailedAlert'));
                $F.log('Workorder3 - takeOver', 'Lock failed, cannot takeover when other is operating.');
                return -1;
            }
            //调用接管前前命令组，校验是否可以接管
            if (wo.instance.struct.version === 'DYN2') {
                if (!wo.checkBeforeOperate('wo.take.prepare')) {
                    return -1;
                }
            } else {
                if (!wo.checkBeforeOperate('P125')) {
                    project.doUnlock(lockKey, '', wo.dbPool);
                    return -1;
                }
            }
            //调用接管前命令组
            if (wo.instance.struct.version === 'DYN2') {
                if (!wo.checkBeforeOperate('wo.take.before')) {
                    return -1;
                }
            } else {
                if (!wo.checkBeforeOperate('P115')) {
                    project.doUnlock(lockKey, '', wo.dbPool);
                    return -1;
                }
            }

            var mNewStep = wo.newStep({
                workorderGuid : wo.guid,
                oldHandleGroupId : wo.getHandleGroupId(),
                oldHandleById : wo.getHandleById(),
                handleGroupId : project.groupId,
                handleById : $E.staff.id,
                workorderStepStatus : (wo.buttonStatus.getCloseFlag() == 0 ? $CONST.WO.WO_SysTick : $CONST.WO.WO_SysTickClose),
                comments : wo.getCurStepComments(),
                startTime : wo.getExpectStartTime(),
                fromStepIdMap : wo.getCurStepIdMap(),
                sendStepInfo : wo.getSendStepInfo()
            });
            if (!$F.isEmpty(wo.oldFrameId)) {
                wo.frameId = wo.oldFrameId;
            }
            wo.setCurStepStatus($CONST.WO.WO_SysOpen);
            wo.setWorkOrderStatus($CONST.WO.WO_SysOpen);
            if (!wo.originalHandleBy) { // 代理人情况不修改处理人和组
                wo.setHandleById($E.staff.id);
                wo.setHandleGroupId(project.groupId);
            }
            wo.setObjectiveStatus('OPEN');

            var mSavemain = wo.taskBoundData;
            mSavemain.SubTableAdd("step", mNewStep);
            wo.operate = 'takeover';
            wo.instance.xmldosRequest(wo.dbPool, mSavemain.getData());
            let saveRet = wo.instance.save();
            wo.instance.xmldosRemoveAll();
            if (saveRet > 0) {
                if (wo.instance.struct.version === 'DYN2') {
                    wo.instance.doCmdIncludeComponents('wo.take.after', {}, wo.instance.runtime, wo.instance.id, true);
                } else {
                    wo.instance.doCmdIncludeComponents("P105");
                }
                wo.loadWorkorderData(wo.guid, wo.getTaskGuid());
                return 1;
            } else {
                project.doUnlock(lockKey, '', wo.dbPool);
                $F.log("Workorder3 - takeOver", "TakeOver failed, please check the server log.");
                return -1;
            }
        },

        /**
         * 工单催单
         * @param flag 为2时候，会保存tabc信息
         * @returns {number}
         */
        cdInfo : function (flag) {
            var wo = this, project = this.project;
            if (wo.buttonStatus.getCDStatus() != 0) {
                $F.alert($F.lang('wo.noCDPriorityAlert'));
                $F.log('Workorder3 - cdInfo', 'No right for cdInfo');
                return -1;
            }
            //调用催单前前命令组，校验是否可以催单
            if (wo.instance.struct.version === 'DYN2') {
                if (!wo.checkBeforeOperate('wo.urge.prepare')) {
                    return -1;
                }
            } else {
                if (!wo.checkBeforeOperate('P121')) {
                    return -1;
                }
            }
            //调用催单前命令组
            if (wo.instance.struct.version === 'DYN2') {
                if (!wo.checkBeforeOperate('wo.urge.before')) {
                    return -1;
                }
            } else {
                if (!wo.checkBeforeOperate('P111')) {
                    return -1;
                }
            }

            var mStepComments = wo.getCurStepComments();
            if ($F.isEmpty(mStepComments)) {
                $F.alert($F.lang('wo.inputCDInfoAlert'));
                return -1;
            }

            var mNewStep = wo.newStep({
                workorderGuid : wo.guid,
                oldHandleGroupId : project.groupId,
                oldHandleById : $E.staff.id,
                handleGroupId : wo.getHandleGroupId(),
                handleById : wo.getHandleById(),
                workorderStepStatus : $CONST.WO.WO_SysURGE,
                comments : mStepComments,
                childId : wo.currentSubStepID,
                startTime : wo.getExpectStartTime(),
                fromStepIdMap : wo.getCurStepIdMap(),
                sendStepInfo : wo.getSendStepInfo()
            });

            if (flag == 2) {
                //添加tabc表的保存操作
                //不用考虑组件中的tabc信息，只需要考虑主页面中的
                $.each(wo.instance.data, function(key, value){
                    if(key !== 'temp') {
                        var tabcName = key.trim().toUpperCase() + $E.staff.epidSql;
                        var tabBD = new BoundData("TABC" + tabcName);
                        tabBD.KeyInfoEdit("Relation_id");
                        tabBD.setValue("Relation_id", wo.getObjectiveGuid());
                        $.each(value, function(fieldName, fieldValue){
                            var prop = wo.instance.getProperty(key.toLowerCase() + '.' + fieldName.toLowerCase());
                            if (prop) {
                                var type = $CONST.BoundType.F_String;
                                /**
                                 * 1: Float
                                 * 2: Date
                                 * 3: String
                                 * 4：Array
                                 * 5：Int
                                 * 6：Object
                                 *
                                 */
                                var propType= wo.struct.version === 'DYN2' ? prop.t : prop.VT;
                                if (propType == 1) {
                                    type = $CONST.BoundType.F_Float;
                                } else if (propType == 2) {
                                    type = $CONST.BoundType.F_Date;
                                } else if (propType == 5) {
                                    type = $CONST.BoundType.F_Int;
                                }
                                tabBD.setValue(fieldName, fieldValue, type);
                            }
                        });
                        mNewStep.SubTableAdd("TAB-" + tabcName, tabBD);
                    }
                });
            }
            var mRs = $F.ajaxSync("SystemDo5", {
                token: project.token,
                dbPool: wo.dbPool,
                xml: mNewStep.getData()
            }, project.digest);
            if (mRs.code != -1) {
                if (wo.instance.struct.version === 'DYN2') {
                    wo.instance.doCmdIncludeComponents('wo.urge.after', {}, wo.instance.runtime, wo.instance.id, true);
                } else {
                    wo.instance.doCmdIncludeComponents("P101");
                }
                wo.clearInfo();
                return 1
            } else {
                $F.alert($F.lang('wo.cdFailedAlert'));
                $F.log('Workorder3 - cdInfo', 'Save cdInfo failed');
                return -1;
            }
        },

        /**
         * 工单协办处理，权限与催单一致，但是可以修改objective和elitetask
         * 协办前前命令组id：130 wo.assist.prepare
         * 协办前命令组id：131 wo.assist.before
         * 协办后命令组id: 132 wo.assist.after
         * Temp.WorkOrderAssistStatus       协办状态属性
         * Temp.WorkOrderAssist             协办操作属性
         * @returns {number}
         */
        assist : function () {
            var wo = this, project = this.project;
            if (wo.buttonStatus.getCDStatus() != 0) {
                $F.alert($F.lang('wo.noAssistPriorityAlert'));
                $F.log('Workorder3 - assist', 'No right for assist');
                return -1;
            }
            //调用协办前前命令组，校验是否可以协办
            if (wo.instance.struct.version === 'DYN2') {
                if (!wo.checkBeforeOperate('wo.assist.prepare')) {
                    return -1;
                }
            } else {
                if (!wo.checkBeforeOperate('P130')) {
                    return -1;
                }
            }
            //调用协办前命令组
            if (wo.instance.struct.version === 'DYN2') {
                if (!wo.checkBeforeOperate('wo.assist.before')) {
                    return -1;
                }
            } else {
                if (!wo.checkBeforeOperate('P131')) {
                    return -1;
                }
            }

            var mStepComments = wo.getCurStepComments();
            if ($F.isEmpty(mStepComments)) {
                $F.alert($F.lang('wo.inputAssistInfoAlert'));
                return -1;
            }

            var mNewStep = wo.newStep({
                workorderGuid : wo.guid,
                oldHandleGroupId : project.groupId,
                oldHandleById : $E.staff.id,
                handleGroupId : wo.getHandleGroupId(),
                handleById : wo.getHandleById(),
                workorderStepStatus : $CONST.WO.WO_SysAssist,
                comments : mStepComments,
                childId : wo.currentSubStepID,
                startTime : wo.getExpectStartTime(),
                fromStepIdMap : wo.getCurStepIdMap(),
                sendStepInfo : wo.getSendStepInfo()
            });
            //保存主动态页面上的tabc
            $.each(wo.instance.data, function(key, value){
                if(key !== 'temp') {
                    var tabcName = key.trim().toUpperCase() + $E.staff.epidSql;
                    var tabBD = new BoundData("TABC" + tabcName);
                    tabBD.KeyInfoEdit("Relation_id");
                    tabBD.setValue("Relation_id", wo.getObjectiveGuid());
                    $.each(value, function(fieldName, fieldValue){
                        var prop = wo.instance.getProperty(key.toLowerCase() + '.' + fieldName.toLowerCase());
                        if (prop) {
                            var type = $CONST.BoundType.F_String;
                            /**
                             * 1: Float
                             * 2: Date
                             * 3: String
                             * 4：Array
                             * 5：Int
                             * 6：Object
                             */
                            var propType= wo.struct.version === 'DYN2' ? prop.t : prop.VT;
                            if (propType == 1) {
                                type = $CONST.BoundType.F_Float;
                            } else if (propType == 2) {
                                type = $CONST.BoundType.F_Date;
                            } else if (propType == 5) {
                                type = $CONST.BoundType.F_Int;
                            }
                            tabBD.setValue(fieldName, fieldValue, type);
                        }
                    });
                    mNewStep.SubTableAdd("TAB-" + tabcName, tabBD);
                }
            });
            //保存objective和elitetask
            //先执行以下getXmlData，处理一下相关的event记录
            wo.objective.getXmlData();
            mNewStep.SubTableAdd('objective', wo.objective.boundData);
            mNewStep.SubTableAdd('elitetask', wo.taskBoundData);

            var mRs = $F.ajaxSync("SystemDo5", {
                token: project.token,
                dbPool: wo.dbPool,
                xml: mNewStep.getData()
            }, project.digest);
            if (mRs.code != -1) {
                if (wo.instance.struct.version === 'DYN2') {
                    wo.instance.doCmdIncludeComponents('wo.assist.after', {}, wo.instance.runtime, wo.instance.id, true);
                } else {
                    wo.instance.doCmdIncludeComponents("P132");
                }
                wo.clearInfo();
                return 1
            } else {
                $F.alert($F.lang('wo.assistFailedAlert'));
                $F.log('Workorder3 - assist', 'Save assist info failed');
                return -1;
            }
        },

        /**
         * 工单回退
         * @returns {number}
         */
        back : function () {
            var wo = this, project = this.project;
            if (wo.buttonStatus.getBackStatus() != 0) {
                $F.alert($F.lang('wo.noBackPriorityAlert'));
                $F.log('Workorder3 - back', 'No right for back.');
                return -1;
            }
            if (wo.validCanOperate() < 0) {
                $F.log('Workorder3 - back', 'No right for back, current handler has changed');
                return -1;
            }
            if (wo.oldFrameId != wo.frameId || wo.oldStepIdMap != wo.getCurStepIdMap()) {
                $F.alert($F.lang('wo.noBackPriorityWOChangedAlert'));
                $F.log('Workorder3 - back', 'Current workorder has changed, cannot back.');
                return -1;
            }
            //调用退单前前命令组，校验是否可以退单
            if (wo.instance.struct.version === 'DYN2') {
                if (!wo.checkBeforeOperate('wo.back.prepare')) {
                    return -1;
                }
            } else {
                if (!wo.checkBeforeOperate('P127')) {
                    return -1;
                }
            }
            //调用退单前命令组
            if (wo.instance.struct.version === 'DYN2') {
                if (!wo.checkBeforeOperate('wo.back.before')) {
                    return -1;
                }
            } else {
                if (!wo.checkBeforeOperate('P117')) {
                    return -1;
                }
            }

            var mBackToGrp, mBackToBy, mBackPathID, mBackStepIDMap, backToRole = '';
            mBackToGrp = wo.getInstancePropertyValue(WOUtils.PROPERTY.BACK_GRP_ID);
            if (mBackToGrp && mBackToGrp.length < 7) {//跟据动态页面的人员处理
                mBackToBy = wo.getInstancePropertyValue(WOUtils.PROPERTY.BACK_STAFF_ID);
            } else {
                mBackToGrp = '';
                mBackToBy = '';
            }
            var backStep = wo.getBackStepObj(wo.guid);
            if (!backStep) {
                $F.alert($F.lang('wo.backFailedNoBackStepFound'));
                $F.log('Workorder3 - back', 'Back failed, cannot found the back step.');
                return -1;
            }

            mBackPathID = backStep.WORKORDERPATH_GUID;
            mBackStepIDMap = backStep.FROM_STEPIDMAP;
            var mCurrentNode = wo.struct.getOneNode(wo.getCurStepIdMap());
            var mBackStepNode = wo.struct.getOneNode(mBackStepIDMap);
            if ($F.isNull(mBackStepNode)) {
                $F.alert($F.lang('wo.backFailedBackStepAlreadyNotExisted'));
                $F.log('Workorder3 - back', 'Back failed, back step is not exists.');
                return -1;
            }
            if ($F.isEmpty(mBackToGrp)) {
                if (mCurrentNode.CANBACKTOBY == 1) {//退给人
                    mBackToGrp = backStep.HANDLEGROUP;
                    mBackToBy = backStep.HANDLEBY;
                } else if (mCurrentNode.CANBACKTOBY == 2) {//退给角色
                    backToRole = backStep.HANDLEROLE_ID;
                    if (!backToRole) {
                        mBackToGrp = backStep.HANDLEGROUP;
                        mBackToBy = backStep.HANDLEBY;
                    } else {
                        mBackToGrp = '';
                        mBackToBy = '';
                    }
                } else {//退给组
                    mBackToGrp = backStep.HANDLEGROUP;
                    mBackToBy = '';
                }
            }
            $F.log('Workorder3 - back', '退单到处理组:' + mBackToGrp + ' 处理人:' + mBackToBy + ' 处理角色: ' + backToRole);
            var mPreBeginTime = wo.getExpectStartTime();
            var mDateBegin = $F.getDbTime();
            var mDateEnd = wo.getNextEndDate(mDateBegin, mBackStepNode.EXPIREDPERIODTYPE_ID, mBackStepNode.EXPIREDPERIOD);
            var mForecastDate = wo.getNextEndDate(mDateEnd, mBackStepNode.FORECASTUNIT, -(mBackStepNode.FORECASTLEN));
            var curStepIdMap = wo.getCurStepIdMap();

            var mHisBound = wo.newStep({
                workorderGuid : wo.guid,
                oldHandleGroupId : project.groupId,
                oldHandleById : $E.staff.id,
                handleGroupId : mBackToGrp,
                handleById : mBackToBy,
                workorderStepStatus : $CONST.WO.WO_SysBack,
                comments : wo.getCurStepComments(),
                stepExpired : mDateEnd,
                startTime : mPreBeginTime,
                fromStepIdMap : curStepIdMap,
                toStepIdMap : mBackStepIDMap,
                sendStepInfo : wo.getSendStepInfo()
            });
            mHisBound.setValue('handlerole_id', backToRole);
            var mDelBound = new BoundData("workorderpath3" + $E.staff.epidSql);
            mDelBound.deleteFlag = 1;
            mDelBound.KeyInfoEdit("WorkOrderPath_GUID");
            mDelBound.setValue("WorkOrderPath_GUID", mBackPathID);
            wo.setObjectiveStatus('OPEN');
            this.setOBFlagByNodeMapId(mBackStepIDMap);
            let backToTask = wo.setNewTaskValue(mBackStepIDMap, mDateBegin, mBackToGrp, mBackToBy, backToRole, mBackStepNode.EXPIREDHANDLEGROUP_ID,
                mBackStepNode.EXPIREDHANDLEBY_ID, mDateEnd, mForecastDate, mBackStepNode.PRIORITY_ID);

            const mSavemain = wo.taskBoundData;
            if (wo.operateTaskGuid) {
                //如果指定了需要操作的taskguid，则检查是insert还是update
                var operateTaskBoundData;
                if (wo.checkTaskExists(wo.operateTaskGuid)) {
                    //如果存在的task，则就更新comments
                    operateTaskBoundData = new BoundData("elitetask" + $E.staff.epidSql);
                    operateTaskBoundData.KeyInfoEdit("elitetask_guid");
                    operateTaskBoundData.setCreateInfo("CREATEDDATE", "CREATEDBY", $E.staff.id);
                    operateTaskBoundData.setModifyInfo("MODIFIEDDATE", "MODIFIEDBY", $E.staff.id);
                    operateTaskBoundData.setValue('comments', wo.getCurStepComments());
                } else {
                    //如果不存在，则需要insert所有字段
                    operateTaskBoundData = backToTask;
                    wo.sendMailOnNewTask(operateTaskBoundData.getValue('handleby_id'));
                    wo.ccOnNewTask(operateTaskBoundData, wo.operateTaskGuid, mBackStepNode);
                }
                operateTaskBoundData.setValue('elitetask_guid', wo.operateTaskGuid);
                mSavemain.SubTableAdd('optask', operateTaskBoundData);
                wo.operateTaskBoundData = operateTaskBoundData;
            } else {
                //如果不指定，就不做任何操作
                //mSavemain.SubTableAdd('optask', backToTask);
            }
            //删除原来的
            wo.deleteTaskAndAddHis(mSavemain);
            mSavemain.SubTableAdd("step", mHisBound);
            mSavemain.SubTableAdd("path", mDelBound);
            wo.deleteAppCall(mSavemain);
            wo.operate = 'back';
            wo.instance.xmldosRequest(wo.dbPool, mSavemain.getData());
            let saveRet = wo.instance.save();
            wo.instance.xmldosRemoveAll();
            if (saveRet > 0) {
                if (wo.instance.struct.version === 'DYN2') {
                    wo.instance.doCmdIncludeComponents('wo.back.after', {}, wo.instance.runtime, wo.instance.id, true);
                } else {
                    wo.instance.doCmdIncludeComponents("P107");
                }
                wo.clearInfo();
                return 1;
            } else {
                wo.restore();
                $F.log('Workorder3 - back', 'Back failed, please check the server log.');
                $F.alert($F.lang('wo.backFailedAlert'));
                return -1;
            }
        },

        /**
         * 工单转交
         * 修改工单处理人处理组，只有当前节点有转交权限，并且当前处理人是自己的时候，才可以转交
         * Temp.WorkOrderTransferStatus      转交状态属性
         * Temp.WorkOrderTransfer            转交操作属性
         * Temp.WoSendNextGroupID            转交给哪个组
         * Temp.WoSendNextStaffID            转交给哪个人
         */
        transfer : function () {
            const wo = this, project = this.project;
            if (wo.buttonStatus.getTransferStatus() != 0) {
                $F.log('Workorder3 - transfer', 'No transfer priority');
                $F.alert($F.lang('wo.noTransferPriorityAlert'));
                return -1;
            }
            if (wo.buttonStatus.getNewFlag() != 1) {
                if (wo.validCanOperate() <= 0) {
                    $F.log('Workorder3 - transfer', 'No transfer priority, operator has been changed.');
                    return -1;
                }
            }

            //调用 "保存前前" 命令组 122，检查是否可以保存
            if (wo.instance.struct.version === 'DYN2') {
                if (!wo.checkBeforeOperate('wo.transfer.prepare')) {
                    return -1;
                }
            }

            //调用保存前命令组
            if (wo.instance.struct.version === 'DYN2') {
                if (!wo.checkBeforeOperate('wo.transfer.before')) {
                    return -1;
                }
            }

            let handleGroup = wo.getInstancePropertyValue(WOUtils.PROPERTY.NEXT_STEP_GROUP_ID);
            let handleBy = wo.getInstancePropertyValue(WOUtils.PROPERTY.NEXT_STEP_STAFF_ID);
            let handleRole = wo.getInstancePropertyValue(WOUtils.PROPERTY.NEXT_STEP_ROLE_ID);
            $F.log('Workorder3 - transfer', 'Transfer to group: ' + handleGroup + ', staff: ' + handleBy + ', role: ' + handleRole);

            let mNewStep, mSavemain;
            mNewStep = wo.newStep({
                workorderGuid : wo.guid,
                oldHandleGroupId : project.groupId,
                oldHandleById : $E.staff.id,
                handleById: handleBy,
                handleGroupId: handleGroup,
                handleRoleId: handleRole,
                workorderStepStatus : $CONST.WO.WO_SysTransfer,
                comments : wo.getCurStepComments(),
                startTime : wo.getExpectStartTime(),
                fromStepIdMap : wo.getCurStepIdMap(),
                toStepIdMap : wo.getCurStepIdMap(),
                sendStepInfo : wo.getSendStepInfo()
            });

            wo.setCurStepStatus($CONST.WO.WO_SysOpen);
            wo.setWorkOrderStatus($CONST.WO.WO_SysOpen);
            this.setOBFlagByNodeMapId(wo.getCurStepIdMap());

            mSavemain = wo.taskBoundData;
            mSavemain.SubTableAdd("step", mNewStep);
            wo.setObjectiveStatus('OPEN');
            // 如果是代理人模式，则不修改处理人


            wo.setHandleGroupId(handleGroup);
            wo.setHandleById(handleBy);
            wo.setHandleRoleId(handleRole);

            wo.operate = 'transfer';
            wo.instance.xmldosRequest(wo.dbPool, mSavemain.getData());
            let transferRet = wo.instance.save();
            wo.instance.xmldosRemoveAll();
            if (transferRet > 0) {
                if (wo.instance.struct.version === 'DYN2') {
                    wo.instance.doCmdIncludeComponents('wo.transfer.after', {}, wo.instance.runtime, wo.instance.id, true);
                }
                wo.clearInfo();
                return 1;
            } else {
                $F.log("Workorder3 - transfer", "Transfer failed, please check the server log.");
                $F.alert($F.lang('wo.transferFailedAlert'));
                return -1;
            }
        },

        //撤单的权限是：1.非当前处理人。2步骤是有撤单权限 3.这个步骤曾经我处理过 要不要加上4.这个工单当然没人打开正在处理中
        //撤单的操作：删除所有task，假装做了个save操作
        /**
         * 撤单操作
         * 撤单前前命令组id: 129
         * 撤单前命令组id：119
         * 撤单后命令组id: 109
         * Temp.WorkOrderRevokeStatus       撤单状态属性
         * Temp.WorkOrderRevoke             撤单操作属性
         * Temp.RevokeToNodeMapId           撤单到哪一个步骤属性
         * 对应的撤单权限的节点扩展属性名为： revokeflag
         * @returns {number}
         */
        revoke : function() {
            const wo = this, project = this.project;
            if (wo.buttonStatus.getRevokeStatus() != 0) {
                $F.log('Workorder3 - revoke', 'No revoke priority');
                $F.alert($F.lang('wo.noRevokePriorityAlert'));
                return -1;
            }
            // 加锁
            var lockRet = wo.doLock(wo.genLockKey());
            if (!lockRet) {
                $F.alert($F.lang('wo.noRevokePriorityAlert'));
                $F.log('Workorder3 - revoke', 'Lock failed, cannot revoke when other is operating.');
                return -1;
            }

            //调用撤单前前命令组，校验是否可以撤单
            if (wo.instance.struct.version === 'DYN2') {
                if (!wo.checkBeforeOperate('wo.off.prepare')) {
                    return -1;
                }
            } else {
                if (!wo.checkBeforeOperate('P129')) {
                    return -1;
                }
            }
            //调用撤单前命令组
            if (wo.instance.struct.version === 'DYN2') {
                if (!wo.checkBeforeOperate('wo.off.before')) {
                    return -1;
                }
            } else {
                if (!wo.checkBeforeOperate('P119')) {
                    return -1;
                }
            }
            var curStepIdMap = wo.getCurStepIdMap();
            var revokeToNodeId = wo.revokeToNodeMapId;
            if (!revokeToNodeId) {
                var ret = project.commonQuerySync('ECore.Workorder.Path3_2', [$E.staff.epidSql, wo.guid, $E.staff.id], wo.dbPool);
                if (ret && ret.value[0].count > 0) {
                    var pathList = $F.makeJsonArray(ret.value[0]);
                    if (pathList.length > 0) {
                        revokeToNodeId = pathList[0].FROM_STEPIDMAP;
                    }
                }
            }
            if (!revokeToNodeId) {
                $F.log('Workorder3 - revoke', 'Cannot revoke, no nodeMapId found.');
                $F.alert($F.lang('wo.noNodeMapFoundForRevoke'));
                return -1;
            }
            var revokeToNode = wo.struct.getOneNode(revokeToNodeId);
            if (!revokeToNode) {
                $F.log('Workorder3 - revoke', 'Cannot revoke, no node found: ' + revokeToNodeId);
                $F.alert($F.lang('wo.noNodeMapFoundForRevoke'));
                return -1;
            }
            $F.log('Workorder3 - revoke', 'Revoke to nodeMapId: ' + revokeToNodeId);


            // 1. 删除所有task
            //ECore.Workorder.deleteTaskByOId = delete from elitetask{C_epid} where objective_guid = @{C1}S
            project.commonUpdateSync('ECore.Workorder.deleteTaskByOId', [wo.guid], wo.dbPool);
            wo.setCurStepStatus($CONST.WO.WO_SysOpen);
            wo.setWorkOrderStatus($CONST.WO.WO_SysOpen);
            wo.setOBFlagByNodeMapId(curStepIdMap);
            if (!wo.originalHandleBy) { // 代理人情况不修改处理人和组
                wo.setHandleById($E.staff.id);
                wo.setHandleGroupId(project.groupId);
            }
            wo.setObjectiveStatus('OPEN');
            var woFrameMap = project.getFrameMap(wo.getFrameMapId());
            wo.setObjectiveTypeId(woFrameMap.workorderFrameId);
            wo.setCurStepIdMap(revokeToNodeId);

            //做相关保存操作
            var mNewStep, mSavemain = wo.taskBoundData;
            mNewStep = wo.newStep({
                workorderGuid : wo.guid,
                oldHandleGroupId : project.groupId,
                oldHandleById : $E.staff.id,
                workorderStepStatus: $CONST.WO.WO_SysRollBack,
                comments : wo.getCurStepComments(),
                startTime : wo.getExpectStartTime(),
                fromStepIdMap : curStepIdMap,
                toStepIdMap : revokeToNodeId,
                sendStepInfo : wo.getSendStepInfo()
            });
            // 保存新的task之前，生成新的cclist
            wo.ccOnNewTask(wo.taskBoundData, wo.taskBoundData.getValue('elitetask_guid'), revokeToNode);

            mSavemain.SubTableAdd("step", mNewStep);
            wo.operate = 'revoke';
            wo.instance.xmldosRequest(wo.dbPool, mSavemain.getData());
            let saveRet = wo.instance.save();
            wo.instance.xmldosRemoveAll();
            if (saveRet > 0) {
                if (wo.instance.struct.version === 'DYN2') {
                    wo.instance.doCmdIncludeComponents('wo.off.after', {}, wo.instance.runtime, wo.instance.id, true);
                } else {
                    wo.instance.doCmdIncludeComponents("P109");
                }
                wo.clearInfo();
                return 1;
            } else {
                $F.log("Workorder3 - revoke", "Revoke failed, please check the server log.");
                $F.alert($F.lang('wo.revokeFailedAlert'));
                return -1;
            }
        },

        // cc已读操作
        ccRead: function() {
            const wo = this, project = this.project;
            if (wo.buttonStatus.getCCReadStatus() != 0) {
                $F.alert($F.lang('wo.noCCReadPriorityAlert'));
                $F.log('Workorder3 - ccRead', 'No right for ccRead');
                return -1;
            }
            //调用催单前前命令组，校验是否可以催单
            if (wo.instance.struct.version === 'DYN2') {
                if (!wo.checkBeforeOperate('wo.ccread.prepare')) {
                    return -1;
                }
            } else {
                if (!wo.checkBeforeOperate('P121')) {
                    return -1;
                }
            }
            //调用催单前命令组
            if (wo.instance.struct.version === 'DYN2') {
                if (!wo.checkBeforeOperate('wo.ccread.before')) {
                    return -1;
                }
            } else {
                if (!wo.checkBeforeOperate('P111')) {
                    return -1;
                }
            }

            // 更新cclist读取状态为“已读”
            project.commonUpdateSync('ECore.Workorder.readCCList', [1, wo.taskBoundData.getValue('elitetask_guid'), $E.staff.id]);

            const mNewStep = wo.newStep({
                workorderGuid : wo.guid,
                oldHandleGroupId : project.groupId,
                oldHandleById : $E.staff.id,
                handleGroupId : wo.getHandleGroupId(),
                handleById : wo.getHandleById(),
                workorderStepStatus : $CONST.WO.WO_SysCCRead,
                childId : wo.currentSubStepID,
                startTime : wo.getExpectStartTime(),
                fromStepIdMap : wo.getCurStepIdMap(),
                sendStepInfo : wo.getSendStepInfo()
            });
            let mRs = $F.ajaxSync("SystemDo5", {
                token: project.token,
                dbPool: wo.dbPool,
                xml: mNewStep.getData()
            }, project.digest);
            if (mRs.code !== -1) {
                if (wo.instance.struct.version === 'DYN2') {
                    wo.instance.doCmdIncludeComponents('wo.ccread.after', {}, wo.instance.runtime, wo.instance.id, true);
                }
                wo.clearInfo();
                return 1
            } else {
                $F.alert($F.lang('wo.ccReadFailedAlert'));
                $F.log('Workorder3 - ccRead', 'Save ccRead failed');
                return -1;
            }
        },

        /**
         * 退单失败时候，恢复相关值
         */
        restore : function () {
            const wo = this;
            wo.taskBoundData = wo.oldTaskBoundData;
            wo.setCurStepComments(wo.taskBoundData.getValue("comments"));
            wo.setCurStepIdMap(wo.taskBoundData.getValue("curstep_id_map"));
        },

        /**
         * 清空信息，关闭tab页
         */
        clearInfo : function () {
            this.buttonStatus.unLockedProcessing(this.guid);
            if (this.continuable) { // 是否继续当前实例，则仅仅隐藏容器
                // if (this.container) {
                //     this.instance.attributes[$CONST.Instance.ON_DESTROY] = [];// 清空onDestroy
                //     delete this.instance.externalListener['WORKORDER_' + this.guid];
                // }
            } else {
                this.project.events.remove(this.instance.id, $CONST.ModuleEvent.ECM_CUSTOMERCHANGE);
                if (this.showOnTab) {
                    this.project.tabClose(this.instance.id);
                } else {
                    if (this.rootInstance) { // 从外层instance中移除自己
                        this.rootInstance.wos.remove(this);
                        this.rootInstance = null;
                    }
                    if (this.container) {
                        this.container.empty();
                        this.container.html('<span>' + $F.lang('wo.workorderFinished') + '</span>');
                        this.container = null;
                    }
                    this.instance.destroy();
                    this.clearInfo = undefined;
                }
                this.instance = null;
                this.project = null;
                if (this.isMobile) {
                    $E.back();
                }
            }
            return 1;
        },

        getNextEndDate : function (date, c, v) {
            return $F.getNextEndDate(date, c, v, this.project.getCache($CONST.CacheName.FREE_DATE), this.project.getParam("WORKHO"));
        },

        validCanOperate : function () {
            const ret = this.project.doLockCheck(this.genLockKey(), '', this.dbPool, true);
            if (ret.code < 0) {
                if (ret.code === -2) {
                    if (ret.message === $E.staff.id) {
                        $F.alert($F.ognl($F.lang('wo.lockedBySelfAlert'), function() {
                            return $F.parseClientFlag(ret.value);
                        }), 0);
                    } else {
                        const staff = this.project.getStaffById(ret.message);
                        $F.alert($F.ognl($F.lang('wo.lockedAlert'), function() {
                            return staff ? staff.name : ret.message;
                        }), 0);
                    }
                } else {
                    $F.alert($F.lang('wo.lockedNoPermission'));
                }
            }
            return ret.code;
        },

        /**
         * 生成锁的key
         */
        genLockKey : function(refresh) {
            if (this.lockKey && !refresh) {
                return this.lockKey;
            }
            let lockKey = 'workorder-' + this.guid;
            if (this.getTaskGuid()) {
                lockKey += '-' + this.getTaskGuid();
            }
            this.lockKey = lockKey;
            return lockKey;
        },

        doLock(lockKey, callback) {
            let WOLALT = this.project.getParam("WOLALT");
            return this.project.doLock(lockKey, '', this.dbPool, callback, WOLALT === 'alert' ? 1 : 0);
        },

        setValue: function(key, value, type) {
            this.woBoundData.setValue(key, value, type);
            if (this.objective) {
                this.objective.setValue(key, value);
            }
        },

        getValue: function(key) {
            return this.woBoundData.getValue(key);
        },
        /**
         * fields from objective
         */
        getPriorityId : function () {
            return this.getValue("priority_id");
        },
        setPriorityId : function (vValue) {
            this.setValue("priority_id", vValue);
        },
        getExpiredDate : function () {
            return this.getValue("expireddate");
        },
        setExpiredDate : function (vValue) {
            this.setValue("expireddate", vValue);
        },
        getForecastDate : function () {
            return this.getValue("forecastdate");
        },
        setForecastDate : function (vValue) {
            this.setValue("forecastdate", vValue, $CONST.BoundType.F_Date);
        },
        setWorkOrderStatus : function (workOrderStatus) {
            this.setValue("objectivestatus", workOrderStatus);
        },
        getWorkOrderStatus : function () {
            return this.getValue("objectivestatus");
        },
        setCategory : function (category) {
            this.setValue('category', category);
        },
        getCategory : function () {
            return this.getValue('category');
        },
        setObjectiveStatus : function (objectiveStatus) {
            this.setValue('objectivestatus', objectiveStatus);
        },
        getCreatedDate : function(){
            return this.getValue('createddate');
        },
        setWorkOrderFrameId : function (workorderframeId) {
            this.setValue('workorderframe_id', workorderframeId);
        },
        getWorkOrderFrameId : function () {
            return this.getValue('workorderframe_id');
        },
        setRolegroupId : function(rolegroupId) {
            this.setValue('rolegroup_id', rolegroupId);
        },
        getRolegroupId : function() {
            return this.getValue('rolegroup_id');
        },

        /**
         * fields from elitetask
         */
        //现在只有stepidmap了，为了兼容老的，所以还是保留setCurStepId和getCurStepId
        setCurStepId : function (stepId) {
            this.setCurStepIdMap(stepId);
        },
        getCurStepId : function () {
            return this.getCurStepIdMap();
        },
        getStepForecastDate : function () {
            return this.taskBoundData.getValue("stepforecastdate");
        },
        setStepForecastDate : function (vValue) {
            this.taskBoundData.setValue("stepforecastdate", vValue, $CONST.BoundType.F_Date);
        },
        setExpectStartTime : function (expectStartTime) {
            this.taskBoundData.setValue("expectstarttime", expectStartTime, $CONST.BoundType.F_Date);
        },
        getExpectStartTime : function () {
            return this.taskBoundData.getValue("expectstarttime");
        },
        setExpiredHandleGroupId : function (expGrpId) {
            this.taskBoundData.setValue("expiredhandlegroup_id", expGrpId);
        },
        getExpiredHandleGroupId : function () {
            return this.taskBoundData.getValue("expiredhandlegroup_id");
        },
        setExpiredHandleById : function (expHandleById) {
            this.taskBoundData.setValue("expiredhandleby_id", expHandleById);
        },
        getExpiredHandleById : function () {
            return this.taskBoundData.getValue("expiredhandleby_id");
        },
        setExpectEndTime : function (expectEndTime) {
            this.taskBoundData.setValue("expectendtime", expectEndTime, $CONST.BoundType.F_Date);
        },
        getExpectEndTime : function () {
            return this.taskBoundData.getValue("expectendtime");
        },
        setCurStepStatus : function (curStepStatus) {
            this.taskBoundData.setValue("curstepstatus", curStepStatus);
        },
        getCurStepStatus : function () {
            return this.taskBoundData.getValue("curstepstatus");
        },
        getCurStepCreatedDate : function () {
            return this.taskBoundData.getValue("createddate");
        },
        setTaskGuid : function(taskGuid){
            this.taskBoundData.setValue("elitetask_guid", taskGuid);
        },
        getTaskGuid : function(){
            return this.taskBoundData.getValue('elitetask_guid');
        },
        setTaskPriority : function(priority){
            this.taskBoundData.setValue("priority", priority);
        },
        getTaskPriority : function(){
            return this.taskBoundData.getValue('priority');
        },

        /**
         * 两个表重复都有的字段
         * @param grpId
         */
        setHandleGroupId : function (grpId) {
            this.setValue("handlegroup_id", grpId);
            this.taskBoundData.setValue("handlegroup_id", grpId);
        },
        getHandleGroupId : function () {
            return this.taskBoundData.getValue("handlegroup_id");
        },
        setHandleById : function (handleById) {
            this.setValue("handleby_id", handleById);
            this.taskBoundData.setValue("handleby_id", handleById);
        },
        getHandleById : function () {
            return this.taskBoundData.getValue("handleby_id");
        },
        setHandleRoleId : function(handleRoleId) {
            this.setValue("handlerole_id", handleRoleId);
            this.taskBoundData.setValue('handlerole_id', handleRoleId);
        },
        getHandleRoleId : function(){
            return this.taskBoundData.getValue('handlerole_id');
        },
        setObjectiveGuid : function (objectiveGuid) {
            this.woBoundData.setValue('objective_guid', objectiveGuid);
            this.taskBoundData.setValue('objective_guid', objectiveGuid);
        },
        getObjectiveGuid : function () {
            return this.woBoundData.getValue('objective_guid');
        },
        setCustomerGuid : function (customerGuid) {
            this.setValue('customer_guid', customerGuid);
            this.taskBoundData.setValue('customer_guid', customerGuid);
        },
        getCustomerGuid : function () {
            return this.woBoundData.getValue('customer_guid');
        },

        setObjectiveSubStatus : function (objectiveSubStatus) {
            this.setValue('objectivesubstatus', objectiveSubStatus);
            this.taskBoundData.setValue('objectivesubstatus', objectiveSubStatus);
        },
        getObjectiveSubStatus : function () {
            return this.taskBoundData.getValue('objectivesubstatus');
        },
        setObjectiveTypeId : function(otId){
            this.taskBoundData.setValue("objectivetype_id", otId);
            this.setValue("objectivetype_id", otId);
        },
        setFrameMapId : function(mapId){
            this.setValue('map_id', mapId);
            this.taskBoundData.setValue("map_id", mapId);
        },
        getFrameMapId : function(){
            return this.getValue('map_id');
        },
        setCurStepIdMap : function (curStepIdMap) {
            this.taskBoundData.setValue("curstep_id_map", curStepIdMap);
            this.setValue("curstep_id_map", curStepIdMap);
        },
        getCurStepIdMap : function () {
            return this.taskBoundData.getValue("curstep_id_map");
        },
        setCurStepComments : function (curStepComments) {
            //暂时保存objective上的comments为task上最新的comments
            this.setValue("comments", curStepComments);
            this.taskBoundData.setValue("comments", curStepComments);
        },
        getCurStepComments : function () {
            return this.taskBoundData.getValue("comments");
        },


        getSendStepInfo : function () {
            return this.stepFieldInfo;
        },
        setSendStepInfo : function (field, value, type) {
            if (!this.stepFieldInfo) {
                this.stepFieldInfo = {};
            }
            var mKey = $F.trimToEmpty(field).toUpperCase();
            if (this.stepFieldInfo.hasOwnProperty(mKey)) {
                delete this.stepFieldInfo[mKey];
            }
            this.stepFieldInfo[mKey] = {
                value: value,
                type: type || $CONST.BoundType.F_String
            };
        },

        /**
         * 设置新的task值
         * @param stepId
         * @param startTime
         * @param handleGroupId
         * @param handleById
         * @param handleRoleId
         * @param expiredHandleGroupId
         * @param expiredHandleById
         * @param stepExpiredDate
         * @param stepForecastDate
         * @returns
         */
        setNewTaskValue : function (stepId, startTime, handleGroupId, handleById, handleRoleId, expiredHandleGroupId, expiredHandleById, stepExpiredDate, stepForecastDate, priority) {
            const wo = this;
            wo.setCurStepIdMap(stepId);
            wo.setExpectStartTime(startTime);
            wo.setExpectEndTime(stepExpiredDate);
            wo.setHandleGroupId(handleGroupId);
            wo.setHandleById(handleById);
            wo.setHandleRoleId(handleRoleId);
            wo.setExpiredHandleGroupId(expiredHandleGroupId);
            wo.setExpiredHandleById(expiredHandleById);
            wo.setStepForecastDate(stepForecastDate);

            const operateTaskBoundData = wo.taskBoundData.copy();
            operateTaskBoundData.deleteFlag = 0; // 确保不是删除操作
            operateTaskBoundData.setValue('elitetask_guid', Guid.raw());
            operateTaskBoundData.setValue('curstep_id', stepId);
            operateTaskBoundData.setValue('expectstarttime', startTime, $CONST.BoundType.F_Date);
            operateTaskBoundData.setValue('expectendtime', stepExpiredDate, $CONST.BoundType.F_Date);
            operateTaskBoundData.setValue('handlegroup_id', handleGroupId);
            operateTaskBoundData.setValue('handleby_id', handleById);
            operateTaskBoundData.setValue('handlerole_id', handleRoleId);
            operateTaskBoundData.setValue('expiredhandlegroup_id', expiredHandleGroupId);
            operateTaskBoundData.setValue('expiredhandleby_id', expiredHandleById);
            operateTaskBoundData.setValue('stepforecastdate', stepForecastDate, $CONST.BoundType.F_Date);
            operateTaskBoundData.setValue('curstep_id_map', stepId);
            operateTaskBoundData.setValue('priority', priority);

            const step = wo.struct.getOneNode(stepId) || {};
            operateTaskBoundData.setValue('objectivesubstatus', step.DEFSUB || '');
            return operateTaskBoundData;
        },

        /**
         * 根据taskId 判断task是否存在
         * @param taskId
         * @returns {Boolean}
         */
        checkTaskExists : function(taskId){
            const ret = this.project.commonQuerySync("ECore.Workorder.selectEliteTask", [taskId], this.dbPool);
            return !$F.isNull(ret) && ret.value[0].count > 0;
        },

        /**
         * 添加elitetask
         * @param tasks {object|array}
         * @param immediate 是否立即插入数据，默认是true
         */
        addTasks : function (tasks, immediate = true) {
            const wo = this;
            if (tasks) {
                let tbds;
                if ($.type(tasks) === 'object') {
                    tasks = [].push(tasks);
                }
                if (immediate) {
                    let now = $F.getDbTime();
                    $.each(tasks, function (i, task) {
                        var tbd = wo.generateTaskBoundData(task, now);
                        if (!tbds) {
                            tbds = tbd;
                        } else {
                            tbds.addSub('task' + i, tbd);
                        }
                        wo.sendMailOnNewTask(tbd.getValue('handleby_id'));
                        wo.ccOnNewTask(tbd, tbd.getValue('elitetask_guid'), wo.struct.getOneNode(tbd.getValue('curstep_id_map')));
                    });

                    if (tbds) {
                        $F.ajaxSync('SystemDo5', {
                            token: wo.project.token,
                            dbPool: wo.dbPool,
                            xml: tbds.getData()
                        }, wo.project.digest);
                    }
                } else {
                    // 非立即执行，加入缓存，等下送时候再一起执行
                    wo.subTasks = tasks;
                }
            }
        },

        /**
         * 给工单的扩展属性赋值
         * @param fieldName 属性名
         * @param value 属性值
         */
        setExtFieldValue : function (fieldName, value) {
            if (fieldName) {
                fieldName = fieldName.trim().toUpperCase();
                if (this.allExtFields.hasOwnProperty(fieldName)) {
                    this.woBoundData.setValue(fieldName, value);
                    if (this.instance) {
                        this.instance.objective.setValue(fieldName, value);
                    }
                }
            }
        },

        /**
         * 获取给定运行时或者当前实例运行时中的变量的值
         * @param propertyName 变量名
         * @param runtime{optional} 运行时，不传递时候就直接用当前实例的运行时
         */
        getInstancePropertyValue : function (propertyName, runtime) {
            if (this.instance) {
                runtime = runtime || this.instance.runtime;
                if (this.instance.getProperty(propertyName, runtime)) {
                    return this.instance.getValue(propertyName, runtime) || '';
                }
            }
            return '';
        },

        addCustomSub : function (handleBy, handleGroup, expiredDate, forecastDate, childId) {
            this.customSubs.push({
                handleBy: handleBy || '',
                handleGroup: handleGroup || '',
                expiredDate: expiredDate,
                forecastDate: forecastDate,
                childId: childId
            });
        },

        /**
         * 构造删除appcall的bounddata并添加的入参的boundData上
         * @param mainBD
         */
        deleteAppCall : function(mainBD){
            if (this.project.getParam("APCALL") == "1") {
                const delAPCall = new BoundData("oblist_appcall" + $E.staff.epidSql);
                delAPCall.deleteFlag = 1;
                delAPCall.KeyInfoEdit("oblist_guid");
                delAPCall.setValue("oblist_guid", this.getTaskGuid());
                mainBD.SubTableAdd("SubDel", delAPCall);
            }
        },

        /**
         * 获取当前流程的自定义扩展属性值
         * @param name
         */
        getCurrentMapExtends : function(name) {
            const mapExtends = this.struct.getStruct().mapExtends;
            if (mapExtends) {
                const mapExtend = mapExtends[name];
                if (mapExtend) {
                    return mapExtend.EXTENDVALUE;
                }
            }
        },

        /**
         * 获取当前节点的自定义扩展属性值
         * @param name
         */
        getCurrentNodeExtends : function(name) {
            const nodeMapId = this.getCurStepIdMap();
            return this.getNodeExtends(nodeMapId, name);
        },

        /**
         * 获取指定节点的自定义扩展属性值
         * @param nodeMapId
         * @param name
         */
        getNodeExtends : function(nodeMapId, name) {
            const node = this.struct.getOneNode(nodeMapId);
            if (node && node.nodeMapExtends) {
                const nodeExtend = node.nodeMapExtends[name];
                if (nodeExtend) {
                    return nodeExtend.EXTENDVALUE;
                }
            }
        },

        /**
         * 根据给定的工单guid和节点id，查询path表历史里这个工单这个步骤的处理人组角色信息
         * @param woGuid 工单guid
         * @param nodeId 节点id
         */
        queryHandlerByNodeId : function (woGuid, nodeId) {
            let handler, data = this.project.commonQuerySync('ECore.Workorder.Path3_1', [woGuid, nodeId], this.dbPool);
            if (data && data.value[0].count > 0) {
                handler = $F.makeJsonBean(data.value[0]);
            }
            return handler;
        },

        queryFirstHandler : function (woGuid) {
            let handler, data = this.project.commonQuerySync('ECore.Workorder.step.selectOldHandleByWOGuid', [woGuid], this.dbPool);
            if (data && data.value[0].count > 0) {
                handler = $F.makeJsonBean(data.value[0]);
            }
            return handler;
        },

        /**
         * 从path上找到最后一条路径，准备根据这个路径退回
         * @returns {object}
         */
        getBackStepObj : function (woGuid) {
            var backStep;
            //Select WorkOrderPath_GUID,FromStepID,HandleGroup,HandleBy,handlerole_id,FromFrameID,From_StepIDMap from workorderpath3{C1} Where WorkOrder_GUID=@{C2}S Order by ModifiedDate desc
            const data = this.project.commonQuerySync("ECore.Workorder.Path3", [$E.staff.epidSql, woGuid], this.dbPool);
            if (data && data.value[0].count > 0) {
                backStep = $F.makeJsonBean(data.value[0]);
            }
            return backStep;
        },

        /**
         * 获取下一个节点
         * @returns {object}
         */
        getNextNode : function() {
            this.nextStepInfo.getNextStepping();
            const nextNode = this.nextStepInfo.getOneNextStep();
            $F.log('workorder3 - getNextNode', 'NextNode: ' + nextNode);
            return nextNode;
        },

        /**
         * 获取上一个节点id
         * @returns {string}
         */
        getPrevNode : function() {
            let prevNodeId = '';
            const backStep = this.getBackStepObj(this.guid);
            if (backStep) {
                prevNodeId = backStep.FROM_STEPIDMAP;
            }
            return prevNodeId;
        },

        /**
         * 根据给定的节点id获取obflag扩展字段的值，并赋值task表的obflag字段
         * @param nodeMapId 节点
         */
        setOBFlagByNodeMapId : function(nodeMapId) {
            const obFlag = this.getNodeExtends(nodeMapId, 'obflag') || 0;
            $F.log('workorder3 - setOBFlagByNodeMapId', 'Set task obflag: ' + obFlag + ' for nodeMapId: ' + nodeMapId);
            this.taskBoundData.setValue('obflag', obFlag, $CONST.BoundType.F_Int);
        },

        /**
         * 新建step，且给所有tabc的his赋值step的guid
         * @param params
         * @returns {object}
         */
        newStep : function(params) {
            params.workorderStepGuid = params.workorderStepGuid || this.woStepGuid;
            if (this.originalHandleBy) {
                params.oldHandleById = this.getOriginalHandleBy(); // 代理人模式下，当前步骤处理人依旧用工单原处理人
                params.oldHandleGroupId = this.originalHandleGroup; // 代理人模式下， 源处理组还是工单原本的处理组
                params.handleStaffId = $E.staff.id; // 真正的处理人
            }
            const stepBD = WOUtils.newStep(params);

            if (this.WODELO) { // 如果需要双写step
                let tabcName = this.instance.fetchTabcName(); // 获取instance相关的一个tabc表表名，不考虑instance关联多个tabc的情况
                // 要检测这个表是不是有，没有的话最好可以自己创建
                let ret;
                $.ajax({
                    method: "POST",
                    contentType: "application/json",
                    url: $E.url + "/wo/cts",
                    async: false,
                    headers: {
                        token: this.project.token
                    },
                    data: JSON.stringify({
                        tabc: tabcName
                    })
                }).done(r => {
                    ret = r;
                });
                if (ret && ret.code === 1) {
                    let tabcStep = stepBD.copy("wo3step_" + tabcName);
                    stepBD.addSub("wo3step_" + tabcName, tabcStep);
                } else {
                    $F.log("Try to create wo3step_" + tabcName + " but failed.")
                }
            }

            this.instance.setHisGuid(this.woStepGuid);
            return stepBD;
        },

        newPath : function(params) {
            if (this.originalHandleBy) {
                params.handleById = this.getOriginalHandleBy();
            }
            return WOUtils.newPath(params);
        },

        getOriginalHandleBy() {
            return this.originalHandleBy === 'force' ? '' : this.originalHandleBy;
        },

        /**
         * 获取下送处理类型
         * @param node
         * @returns {*|string}
         */
        fetchHandleType: function(node) {
            let handleType = this.getInstancePropertyValue(WOUtils.PROPERTY.NEXT_STEP_HANDLE_TYPE);
            if ($F.isEmpty(handleType)) {
                handleType = node.HANDLETYPE;
            }
            return handleType;
        },
        /**
         * 获取下送类型
         * @param node
         * @returns {number}
         */
        fetchSendType: function(node) {
            let sendType = this.getInstancePropertyValue(WOUtils.PROPERTY.NEXT_STEP_SEND_TYPE);
            if ($F.isEmpty(sendType)) {
                sendType = node.SENDTYPE;
            }
            return $F.parseInt(sendType);
        },

        /**
         * 获取下送到原步骤的步骤id
         * @param node
         * @returns {*|string}
         */
        fetchSendOriginalStepId: function(node) {
            let specifiedNode = this.getInstancePropertyValue(WOUtils.PROPERTY.NEXT_STEP_OLD_STEP_ID);
            if (!specifiedNode) {
                specifiedNode = node.HANDLEGROUP_ID;
            }
            return specifiedNode;
        },
        /**
         * 获取下送到组还是人
         * @param node
         * @returns {number}
         */
        fetchSendToGroup: function(node) {
            let sendToGroup = this.getInstancePropertyValue(WOUtils.PROPERTY.NEXT_STEP_SEND_TO_GROUP);
            if (!sendToGroup) {
                sendToGroup = node.RETURNGROUPFLAG;
            }
            return $F.parseInt(sendToGroup);
        },
        /**
         * 获取下送时候的处理组处理人处理角色
         * @param currentStep 当前步骤
         * @param nextStep 下送到的步骤
         * @param handleGroup 下送到的组
         * @param handleType 处理类型
         * @param sendType 下送类型
         * @param handleBy 下送到的人
         * @param handleRole 下送到的角色
         * @returns
         */
        fetchSendHandler : function({currentStep, nextStep, handleType, sendType, handleGroup, handleBy, handleRole}) {
            const wo = this, project = this.project;
            handleType = handleType || wo.fetchHandleType(nextStep);
            sendType = sendType || wo.fetchSendType(nextStep);
            $F.log('Workorder3 - fetchSendHandler', 'handleType: ' + handleType + 'sendType: ' + sendType);
            if (handleType === 'G') { //组
                $F.log('Workorder3 - fetchSendHandler', 'Send Next Step GroupID:' + handleGroup);
                if (handleGroup && handleGroup.length < 7) {//跟据动态页面的人员处理
                    $F.log('Workorder3 - fetchSendHandler', 'Send Next Step StaffID:' + handleBy);
                } else {
                    if (sendType === 0) { //原来的逻辑
                        return this.originalFetchSendHandler(currentStep, nextStep);
                    } else if (sendType === 1) { //指定部门
                        handleGroup = nextStep.HANDLEGROUP_ID;
                        handleBy = '';
                        if (!handleGroup && project.getParam("WODFRO")) { // 如果节点上都没配置部门，取兜底角色
                            handleRole = project.getParam("WODFRO");
                        }
                    } else if (sendType === 2) { //指定人
                        handleGroup = nextStep.HANDLEGROUP_ID;
                        handleBy = nextStep.HANDLEBY_ID;
                        if (!handleGroup && !handleBy && project.getParam("WODFRO")) {
                            // 如果节点上都没配置处理组和处理人，取兜底角色
                            handleRole = project.getParam("WODFRO");
                        }
                    } else if (sendType === 4) { //原步骤(指定步骤)
                        let specifiedNode = wo.fetchSendOriginalStepId(nextStep);
                        let sendToGroup = wo.fetchSendToGroup(nextStep);
                        if (!$F.isEmpty(specifiedNode)) {
                            if (currentStep.NODEMAP_ID === specifiedNode) {
                                handleGroup = project.groupId;
                                if (sendToGroup === 1) {
                                    handleBy = '';
                                } else {
                                    handleBy = $E.staff.id;
                                }
                                $F.log('Workorder3 - fetchSendHandler', '下送处理组:' + handleGroup + ' 处理人:' + handleBy);
                            } else {
                                let oldHandler = wo.queryHandlerByNodeId(wo.guid, specifiedNode);
                                if (oldHandler) {
                                    handleGroup = oldHandler.HANDLEGROUP;
                                    if (sendToGroup === 1) {
                                        handleBy = '';
                                    } else {
                                        handleBy = oldHandler.HANDLEBY;
                                    }
                                    $F.log('Workorder3 - fetchSendHandler', '下送处理组:' + handleGroup + ' 处理人:' + handleBy);
                                } else {
                                    $F.alert($F.lang('wo.noStepFound') + specifiedNode);
                                    $F.log('Workorder3 - fetchSendHandler', '未找到指定步骤历史：' + specifiedNode);
                                    return;
                                }
                            }
                        } else {
                            $F.alert($F.lang('wo.noStepConfigFound'));
                            $F.log('Workorder3 - fetchSendHandler', '未配置指定步骤');
                            return;
                        }
                    } else if (sendType === 7) { //直属上级
                        var data = project.commonQuerySync("ECore.Workorder.queryHigher", [$E.staff.epidSql, wo.getHandleGroupId()], wo.dbPool);
                        if (data && data.value[0].count > 0) {
                            var ars = $F.makeJsonBean(data.value[0]);
                            handleGroup = ars.HIGHER;
                            handleBy = '';
                            $F.log('Workorder3 - fetchSendHandler', '下送处理组:' + handleGroup + ' 处理人:' + handleBy);
                        } else {
                            $F.alert($F.lang('wo.noParentFoundForSpecifiedRolegroup') + wo.getHandleGroupId());
                            $F.log('Workorder3 - fetchSendHandler', '未找到【' + wo.getHandleGroupId() + '】的直属上级部门');
                            return;
                        }
                    } else if (sendType === 9) { //当前组组长
                        var supervisor = this.queryCurrentSupervisor();
                        if (supervisor) {
                            handleGroup = wo.getHandleGroupId();
                            handleBy = supervisor;
                        } else {
                            $F.alert($F.lang('wo.noSupervisorFoundForSpecifiedRolegroup') + wo.getHandleGroupId());
                            $F.log('Workorder3 - fetchSendHandler', '未找到【' + wo.getHandleGroupId() + '】的当前组组长');
                            return;
                        }
                    } else if (sendType === 11) { // 工单发起人
                        let sendToGroup = wo.fetchSendToGroup(nextStep);
                        let firstHandler = wo.queryFirstHandler(wo.guid); // OLDHANDLEBY_ID,OLDHANDLEGROUP_ID,OLDHANDLEROLE_ID
                        if (firstHandler) {
                            handleGroup = firstHandler.OLDHANDLEGROUP_ID;
                            if (sendToGroup === 1) {
                                handleBy = '';
                            } else {
                                handleBy = firstHandler.OLDHANDLEBY_ID;
                            }
                            $F.log('Workorder3 - fetchSendHandler', '下送处理组:' + handleGroup + ' 处理人:' + handleBy);
                        } else {
                            $F.alert('未找到工单发起人');
                            $F.log('Workorder3 - fetchSendHandler', '未找到工单发起人');
                        }
                    } else {
                        $F.alert($F.lang('wo.invalidSendType') + sendType);
                        $F.log('Workorder3 - fetchSendHandler', '不能识别的sendType: ' + sendType);
                        return;
                    }
                }
            } else if (handleType === 'R') { //角色
                handleGroup = '';
                handleBy = '';
                if (!handleRole) {
                    if (sendType === 3) { //指定角色
                        handleRole = nextStep.HANDLEGROUP_ID;
                        if (!handleRole && project.getParam("WODFRO")) {
                            // 如果节点上都没配置角色，取兜底角色
                            handleRole = project.getParam("WODFRO");
                        }
                        $F.log('Workorder3 - fetchSendHandler', '下送处理角色:' + handleRole);
                    } else if (sendType === 4) { //原步骤处理角色
                        let specifiedNode = wo.fetchSendOriginalStepId(nextStep);
                        let sendToGroup = wo.fetchSendToGroup(nextStep);
                        let oldHandler = wo.queryHandlerByNodeId(wo.guid, specifiedNode);
                        if (oldHandler) {
                            handleRole = oldHandler.HANDLEROLE_ID;
                            if (!handleRole) {
                                $F.alert($F.lang('wo.noHandleRoleFoundFromOriginalHandleStep'));
                                $F.log('Workorder3 - fetchSendHandler', '原步骤操作历史记录上未找到处理角色：' + specifiedNode);
                                return;
                            }
                            if (sendToGroup === 0) {
                                handleBy = oldHandler.HANDLEBY;
                            }
                        } else {
                            $F.alert($F.lang('wo.noHistoryFoundForSpecifiedStep'));
                            $F.log('Workorder3 - fetchSendHandler', '未找到指定步骤的历史操作记录：' + specifiedNode);
                            return;
                        }
                    }
                }
            } else if (handleType === 'GR') { //组+角色
                handleBy = '';
                if (sendType === 10) { //指定组 + 角色
                    handleRole = handleRole || nextStep.HANDLEBY_ID;
                    handleGroup = handleGroup || nextStep.HANDLEGROUP_ID;
                    if (!handleGroup && !handleRole && project.getParam("WODFRO")) {
                        // 如果节点上都没配置组和角色，取兜底角色
                        handleGroup = '';
                        handleRole = project.getParam("WODFRO");
                    }
                    $F.log('Workorder3 - fetchSendHandler', '下送处理角色: ' + handleRole + ' 下送处理组: ' + handleGroup);
                } else if (sendType === 4) { //原步骤处理组和角色
                    let specifiedNode = wo.fetchSendOriginalStepId(nextStep);
                    let sendToGroup = wo.fetchSendToGroup(nextStep);
                    let oldHandler = wo.queryHandlerByNodeId(wo.guid, specifiedNode);
                    if (oldHandler) {
                        handleRole = handleRole || oldHandler.HANDLEROLE_ID;
                        handleGroup = handleGroup || oldHandler.HANDLEGROUP;
                        if (!handleRole) {
                            $F.alert($F.lang('wo.noHandleRoleFoundFromOriginalHandleStep'));
                            $F.log('Workorder3 - fetchSendHandler', '原步骤操作历史记录上未找到处理角色：' + specifiedNode);
                            return;
                        }
                        if (sendToGroup === 0) {
                            handleBy = oldHandler.HANDLEBY;
                        }
                    } else {
                        $F.alert($F.lang('wo.noHistoryFoundForSpecifiedStep'));
                        $F.log('Workorder3 - fetchSendHandler', '未找到指定步骤的历史操作记录：' + specifiedNode);
                        return;
                    }
                }
            } else {//老的模式
                $F.log('Workorder3 - fetchSendHandler', 'Send Next Step GroupID:' + handleGroup);
                if (handleGroup && handleGroup.length < 7) {//跟据动态页面的人员处理
                    $F.log('Workorder3 - fetchSendHandler', 'Send Next Step StaffID:' + handleBy);
                } else {
                    return this.originalFetchSendHandler(currentStep, nextStep);
                }
            }

            return {
                handleGroup : handleGroup || '',
                handleBy : handleBy || '',
                handleRole : handleRole || '',
                handleType,
                sendType
            }
        },

        /**
         * 老的获取下送处理人处理组逻辑
         */
        originalFetchSendHandler : function (currentStep, nextStep) {
            const wo = this, project = this.project;
            let handleGroup, handleBy, handleRole;
            if (!$F.isEmpty(nextStep.RETURNOLDSTEP)) {
                if (currentStep.NODEMAP_ID === nextStep.RETURNOLDSTEP) {
                    handleGroup = project.groupId;
                    handleBy = $E.staff.id;
                } else {
                    var oldHandler = wo.queryHandlerByNodeId(wo.guid, nextStep.RETURNOLDSTEP);
                    if (oldHandler) {
                        handleGroup = oldHandler.HANDLEGROUP;
                        if (nextStep.RETURNGROUPFLAG == 1) {
                            handleBy = '';
                        } else {
                            handleBy = oldHandler.HANDLEBY;
                        }
                    } else {
                        $F.alert($F.lang('wo.noHandleByFoundAlert'));
                        $F.log('Workorder3 - send', '未找到指定步骤处理组处理人: ' + nextStep.RETURNOLDSTEP);
                        return -1;
                    }
                }
            } else if (nextStep === wo.getCurStepIdMap() && wo.nextStepInfo.getSendNodeMySelf() == 1) {//发送给自己
                handleGroup = project.groupId;
                handleBy = $E.staff.id;
            } else if (nextStep === wo.getCurStepIdMap() && wo.nextStepInfo.getSendNodeGrpSelf() == 1) { //发送给当前组
                handleGroup = project.groupId;
                handleBy = '';
            } else {
                handleGroup = nextStep.HANDLEGROUP_ID;
                handleBy = nextStep.HANDLEBY_ID;
                if ($F.isEmpty(handleGroup)) {
                    $F.alert($F.lang('wo.noHandleGroupFoundForNextStep') + ' : ' + nextStep.NODEMAPNAME);
                    $F.log('Workorder3 - send', 'No handleGroup found from send next step.');
                    return -1;
                }
            }
            $F.log('Workorder3 - send', '下送处理组: ' + handleGroup + ' 处理人: ' + handleBy);
            return {
                handleGroup : handleGroup,
                handleBy : handleBy,
                handleRole : handleRole || ''
            }
        },

        /**
         * 修改当前的工单实例客户(谨慎使用)
         * @param customerGuid
         */
        changeCustomer : function(customerGuid) {
            if (customerGuid) {
                const instance = this.instance, oldCustomerGuid = this.getCustomerGuid();
                const customer = Customer.createByGuid(this.project, customerGuid, this.dbPool);
                if (customer) {
                    //设置示例客户为新的客户对象
                    instance.customer = customer;
                    //重新对cust_卡头变量做值的绑定
                    let property, ns, field;
                    for (let key in instance.runtime.props.pub) {
                        property = instance.runtime.props.pub[key];
                        ns = (instance.struct.version === 'DYN2' ? property.ns : property.pM);
                        if (ns === 'temp') {
                            field = (instance.struct.version === 'DYN2' ? property.field : property.pS);
                            if (field.startWith('cust_')) {
                                instance.fireValue('temp.' + field, instance.runtime, {
                                    type: 0,
                                    data: instance.customer.getValue(instance.bindCustomerProperties[key])
                                });
                            }
                        }
                    }
                    //设置objective和elitetask的客户guid
                    this.setCustomerGuid(customerGuid);
                    $F.log('Workorder3 - changeCustomer', '工单客户由 [' + oldCustomerGuid + '] 改变为了 [' + customerGuid + ']');
                } else {
                    $F.log('Workorder3 - changeCustomer', '未找到客户 [' + customerGuid + '], 改变工单客户失败');
                }
            }
        },

        /**
         * 当新增task时候，新增抄送
         * @param bound 主bound对象
         * @param taskId
         * @param step 下一个节点
         * @param [link] 走的路径
         */
        ccOnNewTask: function(bound, taskId, step, link) {
            let cclist = [];
            if (step && step.CCTO) {
                cclist = this.fetchCCTO(cclist, step.CCTO);
            }
            if (link && link.CCTO) {
                cclist = this.fetchCCTO(cclist, link.CCTO);
            }
            if (cclist.length > 0) {
                let ccMap = {}, supervisor;
                cclist.forEach(ccto => {
                    let staffId;
                    if (ccto.t === 1) {
                        if (!supervisor)
                            supervisor = this.queryCurrentSupervisor();
                        staffId = supervisor;
                    } else {
                        staffId = ccto.id;
                    }
                    ccMap[staffId] = 1;
                });
                Object.keys(ccMap).forEach(cc => {
                    if (cc) {
                        let cclistBoundData = WOUtils.newCCList({
                            oId: this.guid,
                            taskId,
                            cc
                        });
                        bound.addSub("cclist_" + cc, cclistBoundData);
                    }
                });
            }
        },

        fetchCCTO : function(cclist, CCTO) {
            try {
                let cctos = JSON.parse(CCTO);
                if ($.type(cctos) === 'object') {
                    cclist.push(cctos);
                } else {
                    cclist = cclist.concat(cctos);
                }
            } catch (e) {}
            return cclist;
        },

        /**
         * 查询当前组的组长
         * @returns {*}
         */
        queryCurrentSupervisor: function() {
            const project = this.project;
            const supervisorFlag = (project.getParam('ISSUPE') > 0) ? project.getParam('ISSUPE') : 0;
            const data = project.commonQuerySync("ECore.Workorder.querySupervisor", [this.getHandleGroupId(), supervisorFlag], this.dbPool);
            if (data && data.value[0].count > 0) {
                var ars = $F.makeJsonBean(data.value[0]);
                return ars.STAFF_ID;
            }
        },

        /**
         * 当:
         * 1.工单下送生成新的elitetask时候
         * 2.工单下送同时分配子任务（创建多个elitetask）时候
         * 3.工单回退时候生成新的elitetask时候
         * 4.工单重新打开生成新elitetask时候
         * 判断如果不是操作人自己新增elitetask时候，去判断是否要去发送邮件
         * @param staffId
         */
        sendMailOnNewTask : function(staffId) {
            const wo = this;
            if (staffId && staffId !== $E.staff.id) {
                const staff = wo.project.getStaffById(staffId);
                if (staff && staff.bean.SENDMAILONNEWTASK == 1) {
                    const subject = wo.getCurrentMapExtends('SENDMAILONNEWTASK_SUBJECT'),
                        template = wo.getCurrentMapExtends('SENDMAILONNEWTASK_TEMPLATE');
                    if (subject && template) {
                        let runtime = wo.instance.runtime,
                            R = $.extend({}, runtime.data.pub, runtime.data.local),
                            imp = CmdUtils.getDataObject(wo.project, wo.instance.customer, wo.instance.objective),
                            loc = {};
                        for (let key in R) {
                            if (key !== 'RUNTIME' && key !== 'CTRL' && key !== 'INSTANCE' && key !== 'PROJECT' && key !== 'IEVENT') {
                                loc[key] = R[key];
                            }
                        }
                        imp = $.extend(imp, runtime.data.pub, runtime.data.local, loc);
                        $F.log('Workorder3 - sendMailOnNewTask', 'Call templetMail -  template:' + template + '  subject:' + subject + '  to:' + staff.bean.E_MAIL);
                        MergeUtil.templetMail(imp, template, subject, staff.bean.E_MAIL, '', new Date().format(), null, {}, 0);
                    }
                }
            }
        },

        /**
         * 用户dyn.js调用获取当前鼠标移上去的节点的处理人和处理时间
         */
        getNodeInfo : function(node, callback) {
            //{"NODEMAP_ID":"11D834","NodeName":"普通处理","processed":false}
            let data;
            if (node.stepGuid) {
                data = this.project.commonQuerySync("ECore.Workorder.selectStepByGuid", [node.stepGuid], this.dbPool);
            } else {
                data = this.project.commonQuerySync("ECore.Workorder.selectStepByWOGuidAndStepId", [this.guid, node.NODEMAP_ID], this.dbPool);
            }

            if (data && data.value[0].count > 0) {
                let nodeInfo = $F.makeJsonBean(data.value[0]);
                let staffName = '';
                if (nodeInfo) {
                    if (nodeInfo.OLDHANDLEBY_ID) {
                        let staff = this.project.getStaffById(nodeInfo.OLDHANDLEBY_ID);
                        if (staff)
                            staffName = staff.name;
                    }
                    callback({
                        handleBy: staffName,
                        handleDate: nodeInfo.MODIFIEDDATE
                    })
                } else {
                    callback();
                }
            } else {
                callback();
            }
        },

        /**
         * 删掉原来的elitetask，并把删掉的数据保存到his表中
         * 下送，结案，失败结案，回退时候会有此操作
         * @param elitetask 原task的bounddata
         */
        deleteTaskAndAddHis : function(elitetask) {
            if (this.oldTaskBoundData) {
                const taskHisBD = this.oldTaskBoundData.copy('elitetask_his' + $E.staff.epidSql);
                // $F.ajaxSync('SystemDo5', {
                //     token: this.project.token,
                //     dbPool: this.dbPool,
                //     xml: taskHisBD.getData()
                // }, this.project.digest);
                elitetask.addSub('elitetask_his', taskHisBD)
            }
            elitetask.deleteFlag = 1;
        },

        /**
         * 对指定容器中画当前工单流程
         */
        drawNav : function(container) {
            var runtime = this.instance.runtime;
            if (container.type)
                container = container.dom;
            if (!container)
                return $F.err($F.lang('wo.invalidContainer'));
            if ($('.dyn_cust_nav', container).length === 0) {
                var nav = $("<div class='dyn_cust_nav' />").appendTo(container);
                runtime.instance.WO_SIMPLE_FLOWS = this.struct.WO_SIMPLE_FLOWS;
                runtime.navBar = $('<div style="position:relative;height:100%;width:auto" />').appendTo(nav);

                var jsp = jsPlumb.getInstance({
                        HoverPaintStyle : {
                            strokeStyle : "#1e8151",
                            lineWidth : 2
                        },
                        ConnectionOverlays : [ [ "Arrow", {
                            location : 1,
                            id : "arrow",
                            length : 8,
                            width : 8,
                            foldback : 0.8
                        } ] ],
                        Connector : [ "Flowchart", {
                            curviness : 1
                        } ],
                        Anchors : [ "RightMiddle", "LeftMiddle" ],
                        Container : runtime.navBar
                    }),
                    jsp_paintStyle = {
                        strokeStyle : "#7AB02C",
                        fillStyle : "transparent",
                        radius : 2,
                        lineWidth : 2
                    }, jsp_paintStyleUnprocess = {
                        strokeStyle : "#999999",
                        fillStyle : "transparent",
                        radius : 2,
                        lineWidth : 2
                    }, jsp_endpointStyle = {
                        fillStyle : "#1e8151",
                        radius : 2
                    }, jsp_endpointStyleUnprocess = {
                        fillStyle : "#999999",
                        radius : 2
                    };

                var p, unprocessed = false, lo = 10;
                $.each(runtime.instance.WO_SIMPLE_FLOWS, function(i, node){
                    var el = $('<a href="javascript:;" style="box-sizing: content-box;position: relative; top: 0; display: inline-block" class="dyn_flow_wo ' + (node.processed?'processed':'') + '" />')
                        .text(node.NodeName)
                        .data('entity', node)
                        .css({
                            left: lo + 'px'
                        })
                        .click(function(e) {
                            e.preventDefault();
                            runtime.instance.showWOFlowNode(node, el);
                        })
                        .appendTo(runtime.navBar);
                    lo += 30;
                    if (p !== undefined) {
                        jsp.connect({
                            source: p,
                            target: el,
                            paintStyle: (node.processed?jsp_paintStyle:jsp_paintStyleUnprocess),
                            endpointStyle: (node.processed?jsp_endpointStyle:jsp_endpointStyleUnprocess)
                        })
                    }
                    p = el;

                    Tipped.create(el, {
                        ajax: {
                            url: 'help/tipped',
                            simulator: function(callback, element) {
                                runtime.instance.workorder.getNodeInfo($(element).data('entity'), function(data) {
                                    if (data) {
                                        var html = '<div class="wonode_detail">'
                                            + '<div><span class="handleByField">' + $F.lang('wo.handleBy') + ': </span><span class="handleByValue">' + data.handleBy + '</span></div>'
                                            + '<div><span class="handleDateField">' + $F.lang('wo.handleTime') + ': </span><span class="handleDateValue">' + data.handleDate + '</span></div>'
                                            + '</div>';
                                        callback($(html))
                                    } else {
                                        callback();
                                    }
                                })
                            }
                        },
                        cache: false,
                        skin: 'light',
                        radius: false,
                        padding: false,
                        size: 'large',
                        position: 'bottom'
                    });
                });


                var info = $('<div class="info" />');
                runtime.navBar.parent().parent().append(info);
                runtime.navBar.data('info', info);
            }
        },

        fireButtonStatus() {
            const wo = this;
            wo.setButtonStatus(true);
            const fields = {};
            fields[WOUtils.PROPERTY.SAVE_STATUS] = {type: 0, data: wo.buttonStatus._SaveStatus == 1 ? 1 : 0};
            fields[WOUtils.PROPERTY.SEND_STATUS] = {type: 0, data: wo.buttonStatus._SendStatus == 1 ? 1 : 0};
            fields[WOUtils.PROPERTY.CLOSE_OR_REOPEN_STATUS] = {type: 0, data: wo.buttonStatus._CloseOrOpenStatus == 1 ? 1 : 0};
            fields[WOUtils.PROPERTY.END_STATUS] = {type: 0, data: wo.buttonStatus._EndStatus == 1 ? 1 : 0};
            fields[WOUtils.PROPERTY.REOPEN_STATUS] = {type: 0, data: wo.buttonStatus._ReOpenStatus == 1 ? 1 : 0};
            fields[WOUtils.PROPERTY.CANCEL_STATUS] = {type: 0, data: wo.buttonStatus._CancelStatus == 1 ? 1 : 0};
            fields[WOUtils.PROPERTY.FAILED_STATUS] = {type: 0, data: wo.buttonStatus._FailedStatus == 1 ? 1 : 0};
            fields[WOUtils.PROPERTY.TAKEOVER_STATUS] = {type: 0, data: wo.buttonStatus._TakeOverStatus == 1 ? 1 : 0};
            fields[WOUtils.PROPERTY.CD_STATUS] = {type: 0, data: wo.buttonStatus._CDStatus == 1 ? 1 : 0};
            fields[WOUtils.PROPERTY.ASSIST_STATUS] = {type: 0, data: wo.buttonStatus._AssistStatus == 1 ? 1 : 0};
            fields[WOUtils.PROPERTY.BACK_STATUS] = {type: 0, data: wo.buttonStatus._BackStatus == 1 ? 1 : 0};
            fields[WOUtils.PROPERTY.REPLY_STATUS] = {type: 0, data: wo.buttonStatus._ReplyCustomerStatus == 1 ? 1 : 0};
            fields[WOUtils.PROPERTY.TRANSFER_STATUS] = {type: 0, data: wo.buttonStatus.transferStatus == 1 ? 1 : 0};
            fields[WOUtils.PROPERTY.REVOKE_STATUS] = {type: 0, data: wo.buttonStatus.revokeStatus == 1 ? 1 : 0};
            fields[WOUtils.PROPERTY.CC_READ_STATUS] = {type: 0, data: wo.buttonStatus.ccReadStatus == 1 ? 1 : 0};
            fields[WOUtils.PROPERTY.CLOSE_FLAG] = {type: 0, data: wo.buttonStatus._CloseFlag};
            wo.instance.batchFireValue(fields, true);
        },

        /**
         * 判断当前节点是不是最后一个机构节点，也就是说下一步中，是否有行方的节点
         * @return {boolean}
         */
        isLastJgNode() {
            const nextAvailableSteps = this.nextStepInfo.getNextStepping();
            if (nextAvailableSteps) {
                let availableSteps = Object.keys(nextAvailableSteps);
                if (availableSteps.length > 0) {
                    // HF: 1, JG: 0
                    let nextHfNode = availableSteps.find(stepId => {
                        return this.getNodeExtends(stepId, 'N_TYPE') == 1;
                    });
                    if (nextHfNode) {
                        return true;
                    }
                }
            }
            return false;
        }
    };

    Workorder.create = function(proj, addinId, dbPool) {
        return new Workorder(proj, addinId, dbPool);
    };
    Workorder.fetchWOStruct = function(proj, mapId) {
        return Struct.fetchStruct(proj, mapId);
    };
    Workorder.Struct = Struct;
    Workorder.OBJ_FIELDS = {
        DATE_FIELDS: ['firstcontactdate', 'lastcontactdate', 'expireddate', 'expectstarttime', 'expectendtime', 'obstarttime', 'obendtime', 'lastassigndate', 'forecastdate', 'firstcalldate', 'lastcalldate'],
        NUMBER_FIELDS: []
    };
    Workorder.TASK_FIELDS = {
        DATE_FIELDS: ['lastcalltime', 'expectstarttime', 'expectendtime', 'createddate', 'modifieddate', 'currentexpectstarttime', 'lastchaintime', 'lastassigndate', 'stepforecastdate'],
        NUMBER_FIELDS: ['obflag', 'recordstatus', 'attempt', 'max_attempt', 'phoneindex', 'failure_attempt', 'localtrycount', 'currentphoneattempts']
    };


    Workorder.WOUtils = WOUtils;
    return Workorder;
});